Block patches for softfreeze:

- iotest patches
 - Improve performance of the mirror block job in write-blocking mode
 - Limit memory usage for the backup block job
 - Add discard and write-zeroes support to the NVMe host block driver
 - Fix a bug in the mirror job
 - Prevent the qcow2 driver from creating technically non-compliant qcow2
   v3 images (where there is not enough extra data for snapshot table
   entries)
 - Allow callers of bdrv_truncate() (etc.) to determine whether the file
   must be resized to the exact given size or whether it is OK for block
   devices not to shrink
 -----BEGIN PGP SIGNATURE-----
 
 iQFGBAABCAAwFiEEkb62CjDbPohX0Rgp9AfbAGHVz0AFAl2224ESHG1yZWl0ekBy
 ZWRoYXQuY29tAAoJEPQH2wBh1c9AeXMH/RXKEX4BZYMRKCe41P18tJC9Bl2x0T20
 YeOsZVvpARlr7o/36BF2kGFF4MnL0OQ+9ELuyROX865rk/VL2rWqnHDE5oQM889a
 dFwMs+0zvNbig3iLNcw0H5OkE2mrdM+a1EUdn/lBe/39Z8dPqPxRGqIYHq38Ugdu
 emwSy1nWen7o0f71HRJfyVtI3KcrzXx71FrA/FY2yL/eHz+zRYGZj2SpAdFPkXP/
 lgaz+m0tWhnSW1QzEOXB0Gh69ULt/DczCinYmv5qUY1noW5TPPtiDNCQTts5O4ba
 oJsR3AJv5/l9m65JTmiyQSqnQfPcstrQ5FqOcSnP637cfqUFyWsvdks=
 =L7v1
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2019-10-28' into staging

Block patches for softfreeze:
- iotest patches
- Improve performance of the mirror block job in write-blocking mode
- Limit memory usage for the backup block job
- Add discard and write-zeroes support to the NVMe host block driver
- Fix a bug in the mirror job
- Prevent the qcow2 driver from creating technically non-compliant qcow2
  v3 images (where there is not enough extra data for snapshot table
  entries)
- Allow callers of bdrv_truncate() (etc.) to determine whether the file
  must be resized to the exact given size or whether it is OK for block
  devices not to shrink

# gpg: Signature made Mon 28 Oct 2019 12:13:53 GMT
# gpg:                using RSA key 91BEB60A30DB3E8857D11829F407DB0061D5CF40
# gpg:                issuer "mreitz@redhat.com"
# gpg: Good signature from "Max Reitz <mreitz@redhat.com>" [full]
# Primary key fingerprint: 91BE B60A 30DB 3E88 57D1  1829 F407 DB00 61D5 CF40

* remotes/maxreitz/tags/pull-block-2019-10-28: (69 commits)
  qemu-iotests: restrict 264 to qcow2 only
  Revert "qemu-img: Check post-truncation size"
  block: Pass truncate exact=true where reasonable
  block: Let format drivers pass @exact
  block: Evaluate @exact in protocol drivers
  block: Add @exact parameter to bdrv_co_truncate()
  block: Do not truncate file node when formatting
  block/cor: Drop cor_co_truncate()
  block: Handle filter truncation like native impl.
  iotests: Test qcow2's snapshot table handling
  iotests: Add peek_file* functions
  qcow2: Fix v3 snapshot table entry compliancy
  qcow2: Repair snapshot table with too many entries
  qcow2: Fix overly long snapshot tables
  qcow2: Keep track of the snapshot table length
  qcow2: Fix broken snapshot table entries
  qcow2: Add qcow2_check_fix_snapshot_table()
  qcow2: Separate qcow2_check_read_snapshot_table()
  qcow2: Write v3-compliant snapshot list on upgrade
  qcow2: Put qcow2_upgrade() into its own function
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-10-28 14:40:00 +00:00
commit aaffb85335
84 changed files with 2232 additions and 486 deletions

View file

@ -1178,9 +1178,10 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
return ret;
}
int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags)
int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset,
unsigned int bytes,
QEMUIOVector *qiov, size_t qiov_offset,
BdrvRequestFlags flags)
{
int ret;
BlockDriverState *bs;
@ -1207,11 +1208,19 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
flags |= BDRV_REQ_FUA;
}
ret = bdrv_co_pwritev(blk->root, offset, bytes, qiov, flags);
ret = bdrv_co_pwritev_part(blk->root, offset, bytes, qiov, qiov_offset,
flags);
bdrv_dec_in_flight(bs);
return ret;
}
int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags)
{
return blk_co_pwritev_part(blk, offset, bytes, qiov, 0, flags);
}
typedef struct BlkRwCo {
BlockBackend *blk;
int64_t offset;
@ -2063,15 +2072,15 @@ int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf,
BDRV_REQ_WRITE_COMPRESSED);
}
int blk_truncate(BlockBackend *blk, int64_t offset, PreallocMode prealloc,
Error **errp)
int blk_truncate(BlockBackend *blk, int64_t offset, bool exact,
PreallocMode prealloc, Error **errp)
{
if (!blk_is_available(blk)) {
error_setg(errp, "No medium inserted");
return -ENOMEDIUM;
}
return bdrv_truncate(blk->root, offset, prealloc, errp);
return bdrv_truncate(blk->root, offset, exact, prealloc, errp);
}
static void blk_pdiscard_entry(void *opaque)

View file

@ -18,6 +18,11 @@
#include "qapi/error.h"
#include "block/block-copy.h"
#include "sysemu/block-backend.h"
#include "qemu/units.h"
#define BLOCK_COPY_MAX_COPY_RANGE (16 * MiB)
#define BLOCK_COPY_MAX_BUFFER (1 * MiB)
#define BLOCK_COPY_MAX_MEM (128 * MiB)
static void coroutine_fn block_copy_wait_inflight_reqs(BlockCopyState *s,
int64_t start,
@ -61,6 +66,7 @@ void block_copy_state_free(BlockCopyState *s)
}
bdrv_release_dirty_bitmap(s->copy_bitmap);
shres_destroy(s->mem);
g_free(s);
}
@ -71,8 +77,9 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
BlockCopyState *s;
BdrvDirtyBitmap *copy_bitmap;
uint32_t max_transfer =
MIN_NON_ZERO(INT_MAX, MIN_NON_ZERO(source->bs->bl.max_transfer,
target->bs->bl.max_transfer));
MIN_NON_ZERO(INT_MAX,
MIN_NON_ZERO(source->bs->bl.max_transfer,
target->bs->bl.max_transfer));
copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL,
errp);
@ -89,19 +96,31 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
.cluster_size = cluster_size,
.len = bdrv_dirty_bitmap_size(copy_bitmap),
.write_flags = write_flags,
.mem = shres_create(BLOCK_COPY_MAX_MEM),
};
s->copy_range_size = QEMU_ALIGN_DOWN(max_transfer, cluster_size),
/*
* Set use_copy_range, consider the following:
* 1. Compression is not supported for copy_range.
* 2. copy_range does not respect max_transfer (it's a TODO), so we factor
* that in here. If max_transfer is smaller than the job->cluster_size,
* we do not use copy_range (in that case it's zero after aligning down
* above).
*/
s->use_copy_range =
!(write_flags & BDRV_REQ_WRITE_COMPRESSED) && s->copy_range_size > 0;
if (max_transfer < cluster_size) {
/*
* copy_range does not respect max_transfer. We don't want to bother
* with requests smaller than block-copy cluster size, so fallback to
* buffered copying (read and write respect max_transfer on their
* behalf).
*/
s->use_copy_range = false;
s->copy_size = cluster_size;
} else if (write_flags & BDRV_REQ_WRITE_COMPRESSED) {
/* Compression is not supported for copy_range */
s->use_copy_range = false;
s->copy_size = MAX(cluster_size, BLOCK_COPY_MAX_BUFFER);
} else {
/*
* copy_range does not respect max_transfer (it's a TODO), so we factor
* that in here.
*/
s->use_copy_range = true;
s->copy_size = MIN(MAX(cluster_size, BLOCK_COPY_MAX_COPY_RANGE),
QEMU_ALIGN_DOWN(max_transfer, cluster_size));
}
QLIST_INIT(&s->inflight_reqs);
@ -120,79 +139,71 @@ void block_copy_set_callbacks(
}
/*
* Copy range to target with a bounce buffer and return the bytes copied. If
* error occurred, return a negative error number
* block_copy_do_copy
*
* Do copy of cluser-aligned chunk. @end is allowed to exceed s->len only to
* cover last cluster when s->len is not aligned to clusters.
*
* No sync here: nor bitmap neighter intersecting requests handling, only copy.
*
* Returns 0 on success.
*/
static int coroutine_fn block_copy_with_bounce_buffer(BlockCopyState *s,
int64_t start,
int64_t end,
bool *error_is_read,
void **bounce_buffer)
static int coroutine_fn block_copy_do_copy(BlockCopyState *s,
int64_t start, int64_t end,
bool *error_is_read)
{
int ret;
int nbytes;
int nbytes = MIN(end, s->len) - start;
void *bounce_buffer = NULL;
assert(QEMU_IS_ALIGNED(start, s->cluster_size));
bdrv_reset_dirty_bitmap(s->copy_bitmap, start, s->cluster_size);
nbytes = MIN(s->cluster_size, s->len - start);
if (!*bounce_buffer) {
*bounce_buffer = qemu_blockalign(s->source->bs, s->cluster_size);
assert(QEMU_IS_ALIGNED(end, s->cluster_size));
assert(end < s->len || end == QEMU_ALIGN_UP(s->len, s->cluster_size));
if (s->use_copy_range) {
ret = bdrv_co_copy_range(s->source, start, s->target, start, nbytes,
0, s->write_flags);
if (ret < 0) {
trace_block_copy_copy_range_fail(s, start, ret);
s->use_copy_range = false;
s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER);
/* Fallback to read+write with allocated buffer */
} else {
goto out;
}
}
ret = bdrv_co_pread(s->source, start, nbytes, *bounce_buffer, 0);
/*
* In case of failed copy_range request above, we may proceed with buffered
* request larger than BLOCK_COPY_MAX_BUFFER. Still, further requests will
* be properly limited, so don't care too much.
*/
bounce_buffer = qemu_blockalign(s->source->bs, nbytes);
ret = bdrv_co_pread(s->source, start, nbytes, bounce_buffer, 0);
if (ret < 0) {
trace_block_copy_with_bounce_buffer_read_fail(s, start, ret);
trace_block_copy_read_fail(s, start, ret);
if (error_is_read) {
*error_is_read = true;
}
goto fail;
goto out;
}
ret = bdrv_co_pwrite(s->target, start, nbytes, *bounce_buffer,
ret = bdrv_co_pwrite(s->target, start, nbytes, bounce_buffer,
s->write_flags);
if (ret < 0) {
trace_block_copy_with_bounce_buffer_write_fail(s, start, ret);
trace_block_copy_write_fail(s, start, ret);
if (error_is_read) {
*error_is_read = false;
}
goto fail;
goto out;
}
return nbytes;
fail:
bdrv_set_dirty_bitmap(s->copy_bitmap, start, s->cluster_size);
out:
qemu_vfree(bounce_buffer);
return ret;
}
/*
* Copy range to target and return the bytes copied. If error occurred, return a
* negative error number.
*/
static int coroutine_fn block_copy_with_offload(BlockCopyState *s,
int64_t start,
int64_t end)
{
int ret;
int nr_clusters;
int nbytes;
assert(QEMU_IS_ALIGNED(s->copy_range_size, s->cluster_size));
assert(QEMU_IS_ALIGNED(start, s->cluster_size));
nbytes = MIN(s->copy_range_size, MIN(end, s->len) - start);
nr_clusters = DIV_ROUND_UP(nbytes, s->cluster_size);
bdrv_reset_dirty_bitmap(s->copy_bitmap, start,
s->cluster_size * nr_clusters);
ret = bdrv_co_copy_range(s->source, start, s->target, start, nbytes,
0, s->write_flags);
if (ret < 0) {
trace_block_copy_with_offload_fail(s, start, ret);
bdrv_set_dirty_bitmap(s->copy_bitmap, start,
s->cluster_size * nr_clusters);
return ret;
}
return nbytes;
}
/*
@ -271,7 +282,6 @@ int coroutine_fn block_copy(BlockCopyState *s,
{
int ret = 0;
int64_t end = bytes + start; /* bytes */
void *bounce_buffer = NULL;
int64_t status_bytes;
BlockCopyInFlightReq req;
@ -289,7 +299,7 @@ int coroutine_fn block_copy(BlockCopyState *s,
block_copy_inflight_req_begin(s, &req, start, end);
while (start < end) {
int64_t dirty_end;
int64_t next_zero, chunk_end;
if (!bdrv_dirty_bitmap_get(s->copy_bitmap, start)) {
trace_block_copy_skip(s, start);
@ -297,10 +307,14 @@ int coroutine_fn block_copy(BlockCopyState *s,
continue; /* already copied */
}
dirty_end = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, start,
(end - start));
if (dirty_end < 0) {
dirty_end = end;
chunk_end = MIN(end, start + s->copy_size);
next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, start,
chunk_end - start);
if (next_zero >= 0) {
assert(next_zero > start); /* start is dirty */
assert(next_zero < chunk_end); /* no need to do MIN() */
chunk_end = next_zero;
}
if (s->skip_unallocated) {
@ -311,34 +325,26 @@ int coroutine_fn block_copy(BlockCopyState *s,
continue;
}
/* Clamp to known allocated region */
dirty_end = MIN(dirty_end, start + status_bytes);
chunk_end = MIN(chunk_end, start + status_bytes);
}
trace_block_copy_process(s, start);
if (s->use_copy_range) {
ret = block_copy_with_offload(s, start, dirty_end);
if (ret < 0) {
s->use_copy_range = false;
}
}
if (!s->use_copy_range) {
ret = block_copy_with_bounce_buffer(s, start, dirty_end,
error_is_read, &bounce_buffer);
}
bdrv_reset_dirty_bitmap(s->copy_bitmap, start, chunk_end - start);
co_get_from_shres(s->mem, chunk_end - start);
ret = block_copy_do_copy(s, start, chunk_end, error_is_read);
co_put_to_shres(s->mem, chunk_end - start);
if (ret < 0) {
bdrv_set_dirty_bitmap(s->copy_bitmap, start, chunk_end - start);
break;
}
start += ret;
s->progress_bytes_callback(ret, s->progress_opaque);
s->progress_bytes_callback(chunk_end - start, s->progress_opaque);
start = chunk_end;
ret = 0;
}
if (bounce_buffer) {
qemu_vfree(bounce_buffer);
}
block_copy_inflight_req_end(&req);
return ret;

View file

@ -155,7 +155,7 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
}
if (base_len < len) {
ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL);
ret = blk_truncate(s->base, len, false, PREALLOC_MODE_OFF, NULL);
if (ret) {
goto out;
}
@ -471,7 +471,8 @@ int bdrv_commit(BlockDriverState *bs)
* grow the backing file image if possible. If not possible,
* we must return an error */
if (length > backing_length) {
ret = blk_truncate(backing, length, PREALLOC_MODE_OFF, &local_err);
ret = blk_truncate(backing, length, false, PREALLOC_MODE_OFF,
&local_err);
if (ret < 0) {
error_report_err(local_err);
goto ro_cleanup;

View file

@ -73,13 +73,6 @@ static int64_t cor_getlength(BlockDriverState *bs)
}
static int coroutine_fn cor_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
{
return bdrv_co_truncate(bs->file, offset, prealloc, errp);
}
static int coroutine_fn cor_co_preadv(BlockDriverState *bs,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
@ -139,7 +132,6 @@ static BlockDriver bdrv_copy_on_read = {
.bdrv_child_perm = cor_child_perm,
.bdrv_getlength = cor_getlength,
.bdrv_co_truncate = cor_co_truncate,
.bdrv_co_preadv = cor_co_preadv,
.bdrv_co_pwritev = cor_co_pwritev,

View file

@ -113,8 +113,8 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
* available to the guest, so we must take account of that
* which will be used by the crypto header
*/
return blk_truncate(data->blk, data->size + headerlen, data->prealloc,
errp);
return blk_truncate(data->blk, data->size + headerlen, false,
data->prealloc, errp);
}
@ -297,7 +297,7 @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
}
static int coroutine_fn
block_crypto_co_truncate(BlockDriverState *bs, int64_t offset,
block_crypto_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
PreallocMode prealloc, Error **errp)
{
BlockCrypto *crypto = bs->opaque;
@ -311,7 +311,7 @@ block_crypto_co_truncate(BlockDriverState *bs, int64_t offset,
offset += payload_offset;
return bdrv_co_truncate(bs->file, offset, prealloc, errp);
return bdrv_co_truncate(bs->file, offset, exact, prealloc, errp);
}
static void block_crypto_close(BlockDriverState *bs)

View file

@ -2020,7 +2020,8 @@ raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset,
}
static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
bool exact, PreallocMode prealloc,
Error **errp)
{
BDRVRawState *s = bs->opaque;
struct stat st;
@ -2033,6 +2034,7 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
}
if (S_ISREG(st.st_mode)) {
/* Always resizes to the exact @offset */
return raw_regular_truncate(bs, s->fd, offset, prealloc, errp);
}
@ -2043,7 +2045,12 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
}
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
if (offset > raw_getlength(bs)) {
int64_t cur_length = raw_getlength(bs);
if (offset != cur_length && exact) {
error_setg(errp, "Cannot resize device files");
return -ENOTSUP;
} else if (offset > cur_length) {
error_setg(errp, "Cannot grow device files");
return -EINVAL;
}

View file

@ -468,7 +468,8 @@ static void raw_close(BlockDriverState *bs)
}
static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
bool exact, PreallocMode prealloc,
Error **errp)
{
BDRVRawState *s = bs->opaque;
LONG low, high;

View file

@ -1225,6 +1225,7 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
static coroutine_fn int qemu_gluster_co_truncate(BlockDriverState *bs,
int64_t offset,
bool exact,
PreallocMode prealloc,
Error **errp)
{

View file

@ -3291,8 +3291,12 @@ static void bdrv_parent_cb_resize(BlockDriverState *bs)
/**
* Truncate file to 'offset' bytes (needed only for file protocols)
*
* If 'exact' is true, the file must be resized to exactly the given
* 'offset'. Otherwise, it is sufficient for the node to be at least
* 'offset' bytes in length.
*/
int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset,
int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
PreallocMode prealloc, Error **errp)
{
BlockDriverState *bs = child->bs;
@ -3347,20 +3351,19 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset,
goto out;
}
if (!drv->bdrv_co_truncate) {
if (bs->file && drv->is_filter) {
ret = bdrv_co_truncate(bs->file, offset, prealloc, errp);
goto out;
}
if (drv->bdrv_co_truncate) {
ret = drv->bdrv_co_truncate(bs, offset, exact, prealloc, errp);
} else if (bs->file && drv->is_filter) {
ret = bdrv_co_truncate(bs->file, offset, exact, prealloc, errp);
} else {
error_setg(errp, "Image format driver does not support resize");
ret = -ENOTSUP;
goto out;
}
ret = drv->bdrv_co_truncate(bs, offset, prealloc, errp);
if (ret < 0) {
goto out;
}
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not refresh total sector count");
@ -3382,6 +3385,7 @@ out:
typedef struct TruncateCo {
BdrvChild *child;
int64_t offset;
bool exact;
PreallocMode prealloc;
Error **errp;
int ret;
@ -3390,18 +3394,19 @@ typedef struct TruncateCo {
static void coroutine_fn bdrv_truncate_co_entry(void *opaque)
{
TruncateCo *tco = opaque;
tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc,
tco->errp);
tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->exact,
tco->prealloc, tco->errp);
aio_wait_kick();
}
int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
Error **errp)
int bdrv_truncate(BdrvChild *child, int64_t offset, bool exact,
PreallocMode prealloc, Error **errp)
{
Coroutine *co;
TruncateCo tco = {
.child = child,
.offset = offset,
.exact = exact,
.prealloc = prealloc,
.errp = errp,
.ret = NOT_DONE,

View file

@ -2123,9 +2123,11 @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state)
}
static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
bool exact, PreallocMode prealloc,
Error **errp)
{
IscsiLun *iscsilun = bs->opaque;
int64_t cur_length;
Error *local_err = NULL;
if (prealloc != PREALLOC_MODE_OFF) {
@ -2145,7 +2147,11 @@ static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset,
return -EIO;
}
if (offset > iscsi_getlength(bs)) {
cur_length = iscsi_getlength(bs);
if (offset != cur_length && exact) {
error_setg(errp, "Cannot resize iSCSI devices");
return -ENOTSUP;
} else if (offset > cur_length) {
error_setg(errp, "Cannot grow iSCSI devices");
return -EINVAL;
}

View file

@ -620,11 +620,11 @@ static int mirror_exit_common(Job *job)
{
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
BlockJob *bjob = &s->common;
MirrorBDSOpaque *bs_opaque = s->mirror_top_bs->opaque;
MirrorBDSOpaque *bs_opaque;
AioContext *replace_aio_context = NULL;
BlockDriverState *src = s->mirror_top_bs->backing->bs;
BlockDriverState *target_bs = blk_bs(s->target);
BlockDriverState *mirror_top_bs = s->mirror_top_bs;
BlockDriverState *src;
BlockDriverState *target_bs;
BlockDriverState *mirror_top_bs;
Error *local_err = NULL;
bool abort = job->ret < 0;
int ret = 0;
@ -634,6 +634,11 @@ static int mirror_exit_common(Job *job)
}
s->prepared = true;
mirror_top_bs = s->mirror_top_bs;
bs_opaque = mirror_top_bs->opaque;
src = mirror_top_bs->backing->bs;
target_bs = blk_bs(s->target);
if (bdrv_chain_contains(src, target_bs)) {
bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs);
}
@ -873,8 +878,8 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
}
if (s->bdev_length > base_length) {
ret = blk_truncate(s->target, s->bdev_length, PREALLOC_MODE_OFF,
NULL);
ret = blk_truncate(s->target, s->bdev_length, false,
PREALLOC_MODE_OFF, NULL);
if (ret < 0) {
goto immediate_exit;
}
@ -1181,84 +1186,107 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
QEMUIOVector target_qiov;
uint64_t dirty_offset = offset;
uint64_t dirty_bytes;
int ret;
size_t qiov_offset = 0;
int64_t bitmap_offset, bitmap_end;
if (qiov) {
qemu_iovec_init(&target_qiov, qiov->niov);
if (!QEMU_IS_ALIGNED(offset, job->granularity) &&
bdrv_dirty_bitmap_get(job->dirty_bitmap, offset))
{
/*
* Dirty unaligned padding: ignore it.
*
* Reasoning:
* 1. If we copy it, we can't reset corresponding bit in
* dirty_bitmap as there may be some "dirty" bytes still not
* copied.
* 2. It's already dirty, so skipping it we don't diverge mirror
* progress.
*
* Note, that because of this, guest write may have no contribution
* into mirror converge, but that's not bad, as we have background
* process of mirroring. If under some bad circumstances (high guest
* IO load) background process starve, we will not converge anyway,
* even if each write will contribute, as guest is not guaranteed to
* rewrite the whole disk.
*/
qiov_offset = QEMU_ALIGN_UP(offset, job->granularity) - offset;
if (bytes <= qiov_offset) {
/* nothing to do after shrink */
return;
}
offset += qiov_offset;
bytes -= qiov_offset;
}
while (true) {
bool valid_area;
int ret;
if (!QEMU_IS_ALIGNED(offset + bytes, job->granularity) &&
bdrv_dirty_bitmap_get(job->dirty_bitmap, offset + bytes - 1))
{
uint64_t tail = (offset + bytes) % job->granularity;
bdrv_dirty_bitmap_lock(job->dirty_bitmap);
dirty_bytes = MIN(offset + bytes - dirty_offset, INT_MAX);
valid_area = bdrv_dirty_bitmap_next_dirty_area(job->dirty_bitmap,
&dirty_offset,
&dirty_bytes);
if (!valid_area) {
bdrv_dirty_bitmap_unlock(job->dirty_bitmap);
break;
if (bytes <= tail) {
/* nothing to do after shrink */
return;
}
bytes -= tail;
}
bdrv_reset_dirty_bitmap_locked(job->dirty_bitmap,
dirty_offset, dirty_bytes);
bdrv_dirty_bitmap_unlock(job->dirty_bitmap);
/*
* Tails are either clean or shrunk, so for bitmap resetting
* we safely align the range down.
*/
bitmap_offset = QEMU_ALIGN_UP(offset, job->granularity);
bitmap_end = QEMU_ALIGN_DOWN(offset + bytes, job->granularity);
if (bitmap_offset < bitmap_end) {
bdrv_reset_dirty_bitmap(job->dirty_bitmap, bitmap_offset,
bitmap_end - bitmap_offset);
}
job_progress_increase_remaining(&job->common.job, dirty_bytes);
job_progress_increase_remaining(&job->common.job, bytes);
assert(dirty_offset - offset <= SIZE_MAX);
if (qiov) {
qemu_iovec_reset(&target_qiov);
qemu_iovec_concat(&target_qiov, qiov,
dirty_offset - offset, dirty_bytes);
}
switch (method) {
case MIRROR_METHOD_COPY:
ret = blk_co_pwritev_part(job->target, offset, bytes,
qiov, qiov_offset, flags);
break;
switch (method) {
case MIRROR_METHOD_COPY:
ret = blk_co_pwritev(job->target, dirty_offset, dirty_bytes,
qiov ? &target_qiov : NULL, flags);
break;
case MIRROR_METHOD_ZERO:
assert(!qiov);
ret = blk_co_pwrite_zeroes(job->target, offset, bytes, flags);
break;
case MIRROR_METHOD_ZERO:
assert(!qiov);
ret = blk_co_pwrite_zeroes(job->target, dirty_offset, dirty_bytes,
flags);
break;
case MIRROR_METHOD_DISCARD:
assert(!qiov);
ret = blk_co_pdiscard(job->target, offset, bytes);
break;
case MIRROR_METHOD_DISCARD:
assert(!qiov);
ret = blk_co_pdiscard(job->target, dirty_offset, dirty_bytes);
break;
default:
abort();
}
default:
abort();
}
if (ret >= 0) {
job_progress_update(&job->common.job, bytes);
} else {
BlockErrorAction action;
if (ret >= 0) {
job_progress_update(&job->common.job, dirty_bytes);
} else {
BlockErrorAction action;
/*
* We failed, so we should mark dirty the whole area, aligned up.
* Note that we don't care about shrunk tails if any: they were dirty
* at function start, and they must be still dirty, as we've locked
* the region for in-flight op.
*/
bitmap_offset = QEMU_ALIGN_DOWN(offset, job->granularity);
bitmap_end = QEMU_ALIGN_UP(offset + bytes, job->granularity);
bdrv_set_dirty_bitmap(job->dirty_bitmap, bitmap_offset,
bitmap_end - bitmap_offset);
job->actively_synced = false;
bdrv_set_dirty_bitmap(job->dirty_bitmap, dirty_offset, dirty_bytes);
job->actively_synced = false;
action = mirror_error_action(job, false, -ret);
if (action == BLOCK_ERROR_ACTION_REPORT) {
if (!job->ret) {
job->ret = ret;
}
break;
action = mirror_error_action(job, false, -ret);
if (action == BLOCK_ERROR_ACTION_REPORT) {
if (!job->ret) {
job->ret = ret;
}
}
dirty_offset += dirty_bytes;
}
if (qiov) {
qemu_iovec_destroy(&target_qiov);
}
}
@ -1465,15 +1493,6 @@ static void bdrv_mirror_top_child_perm(BlockDriverState *bs, BdrvChild *c,
*nshared = BLK_PERM_ALL;
}
static void bdrv_mirror_top_refresh_limits(BlockDriverState *bs, Error **errp)
{
MirrorBDSOpaque *s = bs->opaque;
if (s && s->job && s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING) {
bs->bl.request_alignment = s->job->granularity;
}
}
/* Dummy node that provides consistent read to its users without requiring it
* from its backing file and that allows writes on the backing file chain. */
static BlockDriver bdrv_mirror_top = {
@ -1486,7 +1505,6 @@ static BlockDriver bdrv_mirror_top = {
.bdrv_co_block_status = bdrv_co_block_status_from_backing,
.bdrv_refresh_filename = bdrv_mirror_top_refresh_filename,
.bdrv_child_perm = bdrv_mirror_top_child_perm,
.bdrv_refresh_limits = bdrv_mirror_top_refresh_limits,
};
static BlockJob *mirror_start_job(
@ -1634,29 +1652,13 @@ static BlockJob *mirror_start_job(
s->should_complete = true;
}
/*
* Must be called before we start tracking writes, but after
*
* ((MirrorBlockJob *)
* ((MirrorBDSOpaque *)
* mirror_top_bs->opaque
* )->job
* )->copy_mode
*
* has the correct value.
* (We start tracking writes as of the following
* bdrv_create_dirty_bitmap() call.)
*/
bdrv_refresh_limits(mirror_top_bs, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto fail;
}
s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
if (!s->dirty_bitmap) {
goto fail;
}
if (s->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING) {
bdrv_disable_dirty_bitmap(s->dirty_bitmap);
}
ret = block_job_add_bdrv(&s->common, "source", bs, 0,
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE |

View file

@ -752,7 +752,7 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
}
static int coroutine_fn
nfs_file_co_truncate(BlockDriverState *bs, int64_t offset,
nfs_file_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
PreallocMode prealloc, Error **errp)
{
NFSClient *client = bs->opaque;

View file

@ -112,6 +112,9 @@ typedef struct {
uint64_t max_transfer;
bool plugged;
bool supports_write_zeroes;
bool supports_discard;
CoMutex dma_map_lock;
CoQueue dma_flush_queue;
@ -423,6 +426,7 @@ static void nvme_identify(BlockDriverState *bs, int namespace, Error **errp)
NvmeIdNs *idns;
NvmeLBAF *lbaf;
uint8_t *resp;
uint16_t oncs;
int r;
uint64_t iova;
NvmeCmd cmd = {
@ -460,6 +464,10 @@ static void nvme_identify(BlockDriverState *bs, int namespace, Error **errp)
s->max_transfer = MIN_NON_ZERO(s->max_transfer,
s->page_size / sizeof(uint64_t) * s->page_size);
oncs = le16_to_cpu(idctrl->oncs);
s->supports_write_zeroes = !!(oncs & NVME_ONCS_WRITE_ZEROS);
s->supports_discard = !!(oncs & NVME_ONCS_DSM);
memset(resp, 0, 4096);
cmd.cdw10 = 0;
@ -472,6 +480,12 @@ static void nvme_identify(BlockDriverState *bs, int namespace, Error **errp)
s->nsze = le64_to_cpu(idns->nsze);
lbaf = &idns->lbaf[NVME_ID_NS_FLBAS_INDEX(idns->flbas)];
if (NVME_ID_NS_DLFEAT_WRITE_ZEROES(idns->dlfeat) &&
NVME_ID_NS_DLFEAT_READ_BEHAVIOR(idns->dlfeat) ==
NVME_ID_NS_DLFEAT_READ_BEHAVIOR_ZEROES) {
bs->supported_write_flags |= BDRV_REQ_MAY_UNMAP;
}
if (lbaf->ms) {
error_setg(errp, "Namespaces with metadata are not yet supported");
goto out;
@ -766,6 +780,8 @@ static int nvme_file_open(BlockDriverState *bs, QDict *options, int flags,
int ret;
BDRVNVMeState *s = bs->opaque;
bs->supported_write_flags = BDRV_REQ_FUA;
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &error_abort);
device = qemu_opt_get(opts, NVME_BLOCK_OPT_DEVICE);
@ -794,7 +810,6 @@ static int nvme_file_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
}
bs->supported_write_flags = BDRV_REQ_FUA;
return 0;
fail:
nvme_close(bs);
@ -1088,6 +1103,140 @@ static coroutine_fn int nvme_co_flush(BlockDriverState *bs)
}
static coroutine_fn int nvme_co_pwrite_zeroes(BlockDriverState *bs,
int64_t offset,
int bytes,
BdrvRequestFlags flags)
{
BDRVNVMeState *s = bs->opaque;
NVMeQueuePair *ioq = s->queues[1];
NVMeRequest *req;
uint32_t cdw12 = ((bytes >> s->blkshift) - 1) & 0xFFFF;
if (!s->supports_write_zeroes) {
return -ENOTSUP;
}
NvmeCmd cmd = {
.opcode = NVME_CMD_WRITE_ZEROS,
.nsid = cpu_to_le32(s->nsid),
.cdw10 = cpu_to_le32((offset >> s->blkshift) & 0xFFFFFFFF),
.cdw11 = cpu_to_le32(((offset >> s->blkshift) >> 32) & 0xFFFFFFFF),
};
NVMeCoData data = {
.ctx = bdrv_get_aio_context(bs),
.ret = -EINPROGRESS,
};
if (flags & BDRV_REQ_MAY_UNMAP) {
cdw12 |= (1 << 25);
}
if (flags & BDRV_REQ_FUA) {
cdw12 |= (1 << 30);
}
cmd.cdw12 = cpu_to_le32(cdw12);
trace_nvme_write_zeroes(s, offset, bytes, flags);
assert(s->nr_queues > 1);
req = nvme_get_free_req(ioq);
assert(req);
nvme_submit_command(s, ioq, req, &cmd, nvme_rw_cb, &data);
data.co = qemu_coroutine_self();
while (data.ret == -EINPROGRESS) {
qemu_coroutine_yield();
}
trace_nvme_rw_done(s, true, offset, bytes, data.ret);
return data.ret;
}
static int coroutine_fn nvme_co_pdiscard(BlockDriverState *bs,
int64_t offset,
int bytes)
{
BDRVNVMeState *s = bs->opaque;
NVMeQueuePair *ioq = s->queues[1];
NVMeRequest *req;
NvmeDsmRange *buf;
QEMUIOVector local_qiov;
int ret;
NvmeCmd cmd = {
.opcode = NVME_CMD_DSM,
.nsid = cpu_to_le32(s->nsid),
.cdw10 = cpu_to_le32(0), /*number of ranges - 0 based*/
.cdw11 = cpu_to_le32(1 << 2), /*deallocate bit*/
};
NVMeCoData data = {
.ctx = bdrv_get_aio_context(bs),
.ret = -EINPROGRESS,
};
if (!s->supports_discard) {
return -ENOTSUP;
}
assert(s->nr_queues > 1);
buf = qemu_try_blockalign0(bs, s->page_size);
if (!buf) {
return -ENOMEM;
}
buf->nlb = cpu_to_le32(bytes >> s->blkshift);
buf->slba = cpu_to_le64(offset >> s->blkshift);
buf->cattr = 0;
qemu_iovec_init(&local_qiov, 1);
qemu_iovec_add(&local_qiov, buf, 4096);
req = nvme_get_free_req(ioq);
assert(req);
qemu_co_mutex_lock(&s->dma_map_lock);
ret = nvme_cmd_map_qiov(bs, &cmd, req, &local_qiov);
qemu_co_mutex_unlock(&s->dma_map_lock);
if (ret) {
req->busy = false;
goto out;
}
trace_nvme_dsm(s, offset, bytes);
nvme_submit_command(s, ioq, req, &cmd, nvme_rw_cb, &data);
data.co = qemu_coroutine_self();
while (data.ret == -EINPROGRESS) {
qemu_coroutine_yield();
}
qemu_co_mutex_lock(&s->dma_map_lock);
ret = nvme_cmd_unmap_qiov(bs, &local_qiov);
qemu_co_mutex_unlock(&s->dma_map_lock);
if (ret) {
goto out;
}
ret = data.ret;
trace_nvme_dsm_done(s, offset, bytes, ret);
out:
qemu_iovec_destroy(&local_qiov);
qemu_vfree(buf);
return ret;
}
static int nvme_reopen_prepare(BDRVReopenState *reopen_state,
BlockReopenQueue *queue, Error **errp)
{
@ -1192,6 +1341,10 @@ static BlockDriver bdrv_nvme = {
.bdrv_co_preadv = nvme_co_preadv,
.bdrv_co_pwritev = nvme_co_pwritev,
.bdrv_co_pwrite_zeroes = nvme_co_pwrite_zeroes,
.bdrv_co_pdiscard = nvme_co_pdiscard,
.bdrv_co_flush_to_disk = nvme_co_flush,
.bdrv_reopen_prepare = nvme_reopen_prepare,

View file

@ -203,7 +203,7 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num,
} else {
ret = bdrv_truncate(bs->file,
(s->data_end + space) << BDRV_SECTOR_BITS,
PREALLOC_MODE_OFF, NULL);
false, PREALLOC_MODE_OFF, NULL);
}
if (ret < 0) {
return ret;
@ -487,7 +487,12 @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs,
res->leaks += count;
if (fix & BDRV_FIX_LEAKS) {
Error *local_err = NULL;
ret = bdrv_truncate(bs->file, res->image_end_offset,
/*
* In order to really repair the image, we must shrink it.
* That means we have to pass exact=true.
*/
ret = bdrv_truncate(bs->file, res->image_end_offset, true,
PREALLOC_MODE_OFF, &local_err);
if (ret < 0) {
error_report_err(local_err);
@ -563,11 +568,6 @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
blk_set_allow_write_beyond_eof(blk, true);
/* Create image format */
ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
if (ret < 0) {
goto out;
}
bat_entries = DIV_ROUND_UP(total_size, cl_size);
bat_sectors = DIV_ROUND_UP(bat_entry_off(bat_entries), cl_size);
bat_sectors = (bat_sectors * cl_size) >> BDRV_SECTOR_BITS;
@ -885,7 +885,9 @@ static void parallels_close(BlockDriverState *bs)
if ((bs->open_flags & BDRV_O_RDWR) && !(bs->open_flags & BDRV_O_INACTIVE)) {
s->header->inuse = 0;
parallels_update_header(bs);
bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS,
/* errors are ignored, so we might as well pass exact=true */
bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS, true,
PREALLOC_MODE_OFF, NULL);
}

View file

@ -480,7 +480,7 @@ static int get_cluster_offset(BlockDriverState *bs,
return -E2BIG;
}
ret = bdrv_truncate(bs->file, cluster_offset + s->cluster_size,
PREALLOC_MODE_OFF, NULL);
false, PREALLOC_MODE_OFF, NULL);
if (ret < 0) {
return ret;
}
@ -858,11 +858,6 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
blk_set_allow_write_beyond_eof(qcow_blk, true);
/* Create image format */
ret = blk_truncate(qcow_blk, 0, PREALLOC_MODE_OFF, errp);
if (ret < 0) {
goto exit;
}
memset(&header, 0, sizeof(header));
header.magic = cpu_to_be32(QCOW_MAGIC);
header.version = cpu_to_be32(QCOW_VERSION);
@ -1038,7 +1033,7 @@ static int qcow_make_empty(BlockDriverState *bs)
if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table,
l1_length) < 0)
return -1;
ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length,
ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length, false,
PREALLOC_MODE_OFF, NULL);
if (ret < 0)
return ret;

View file

@ -2016,7 +2016,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
goto resize_fail;
}
ret = bdrv_truncate(bs->file, offset + s->cluster_size,
ret = bdrv_truncate(bs->file, offset + s->cluster_size, false,
PREALLOC_MODE_OFF, &local_err);
if (ret < 0) {
error_report_err(local_err);

View file

@ -29,29 +29,64 @@
#include "qemu/error-report.h"
#include "qemu/cutils.h"
static void qcow2_free_single_snapshot(BlockDriverState *bs, int i)
{
BDRVQcow2State *s = bs->opaque;
assert(i >= 0 && i < s->nb_snapshots);
g_free(s->snapshots[i].name);
g_free(s->snapshots[i].id_str);
g_free(s->snapshots[i].unknown_extra_data);
memset(&s->snapshots[i], 0, sizeof(s->snapshots[i]));
}
void qcow2_free_snapshots(BlockDriverState *bs)
{
BDRVQcow2State *s = bs->opaque;
int i;
for(i = 0; i < s->nb_snapshots; i++) {
g_free(s->snapshots[i].name);
g_free(s->snapshots[i].id_str);
qcow2_free_single_snapshot(bs, i);
}
g_free(s->snapshots);
s->snapshots = NULL;
s->nb_snapshots = 0;
}
int qcow2_read_snapshots(BlockDriverState *bs)
/*
* If @repair is true, try to repair a broken snapshot table instead
* of just returning an error:
*
* - If the snapshot table was too long, set *nb_clusters_reduced to
* the number of snapshots removed off the end.
* The caller will update the on-disk nb_snapshots accordingly;
* this leaks clusters, but is safe.
* (The on-disk information must be updated before
* qcow2_check_refcounts(), because that function relies on
* s->nb_snapshots to reflect the on-disk value.)
*
* - If there were snapshots with too much extra metadata, increment
* *extra_data_dropped for each.
* This requires the caller to eventually rewrite the whole snapshot
* table, which requires cluster allocation. Therefore, this should
* be done only after qcow2_check_refcounts() made sure the refcount
* structures are valid.
* (In the meantime, the image is still valid because
* qcow2_check_refcounts() does not do anything with snapshots'
* extra data.)
*/
static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair,
int *nb_clusters_reduced,
int *extra_data_dropped,
Error **errp)
{
BDRVQcow2State *s = bs->opaque;
QCowSnapshotHeader h;
QCowSnapshotExtraData extra;
QCowSnapshot *sn;
int i, id_str_size, name_size;
int64_t offset;
uint32_t extra_data_size;
int64_t offset, pre_sn_offset;
uint64_t table_length = 0;
int ret;
if (!s->nb_snapshots) {
@ -64,10 +99,16 @@ int qcow2_read_snapshots(BlockDriverState *bs)
s->snapshots = g_new0(QCowSnapshot, s->nb_snapshots);
for(i = 0; i < s->nb_snapshots; i++) {
bool truncate_unknown_extra_data = false;
pre_sn_offset = offset;
table_length = ROUND_UP(table_length, 8);
/* Read statically sized part of the snapshot header */
offset = ROUND_UP(offset, 8);
ret = bdrv_pread(bs->file, offset, &h, sizeof(h));
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to read snapshot table");
goto fail;
}
@ -79,33 +120,77 @@ int qcow2_read_snapshots(BlockDriverState *bs)
sn->date_sec = be32_to_cpu(h.date_sec);
sn->date_nsec = be32_to_cpu(h.date_nsec);
sn->vm_clock_nsec = be64_to_cpu(h.vm_clock_nsec);
extra_data_size = be32_to_cpu(h.extra_data_size);
sn->extra_data_size = be32_to_cpu(h.extra_data_size);
id_str_size = be16_to_cpu(h.id_str_size);
name_size = be16_to_cpu(h.name_size);
/* Read extra data */
if (sn->extra_data_size > QCOW_MAX_SNAPSHOT_EXTRA_DATA) {
if (!repair) {
ret = -EFBIG;
error_setg(errp, "Too much extra metadata in snapshot table "
"entry %i", i);
error_append_hint(errp, "You can force-remove this extra "
"metadata with qemu-img check -r all\n");
goto fail;
}
fprintf(stderr, "Discarding too much extra metadata in snapshot "
"table entry %i (%" PRIu32 " > %u)\n",
i, sn->extra_data_size, QCOW_MAX_SNAPSHOT_EXTRA_DATA);
(*extra_data_dropped)++;
truncate_unknown_extra_data = true;
}
/* Read known extra data */
ret = bdrv_pread(bs->file, offset, &extra,
MIN(sizeof(extra), extra_data_size));
MIN(sizeof(extra), sn->extra_data_size));
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to read snapshot table");
goto fail;
}
offset += extra_data_size;
offset += MIN(sizeof(extra), sn->extra_data_size);
if (extra_data_size >= 8) {
if (sn->extra_data_size >= endof(QCowSnapshotExtraData,
vm_state_size_large)) {
sn->vm_state_size = be64_to_cpu(extra.vm_state_size_large);
}
if (extra_data_size >= 16) {
if (sn->extra_data_size >= endof(QCowSnapshotExtraData, disk_size)) {
sn->disk_size = be64_to_cpu(extra.disk_size);
} else {
sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE;
}
if (sn->extra_data_size > sizeof(extra)) {
uint64_t extra_data_end;
size_t unknown_extra_data_size;
extra_data_end = offset + sn->extra_data_size - sizeof(extra);
if (truncate_unknown_extra_data) {
sn->extra_data_size = QCOW_MAX_SNAPSHOT_EXTRA_DATA;
}
/* Store unknown extra data */
unknown_extra_data_size = sn->extra_data_size - sizeof(extra);
sn->unknown_extra_data = g_malloc(unknown_extra_data_size);
ret = bdrv_pread(bs->file, offset, sn->unknown_extra_data,
unknown_extra_data_size);
if (ret < 0) {
error_setg_errno(errp, -ret,
"Failed to read snapshot table");
goto fail;
}
offset = extra_data_end;
}
/* Read snapshot ID */
sn->id_str = g_malloc(id_str_size + 1);
ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size);
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to read snapshot table");
goto fail;
}
offset += id_str_size;
@ -115,14 +200,47 @@ int qcow2_read_snapshots(BlockDriverState *bs)
sn->name = g_malloc(name_size + 1);
ret = bdrv_pread(bs->file, offset, sn->name, name_size);
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to read snapshot table");
goto fail;
}
offset += name_size;
sn->name[name_size] = '\0';
if (offset - s->snapshots_offset > QCOW_MAX_SNAPSHOTS_SIZE) {
ret = -EFBIG;
goto fail;
/* Note that the extra data may have been truncated */
table_length += sizeof(h) + sn->extra_data_size + id_str_size +
name_size;
if (!repair) {
assert(table_length == offset - s->snapshots_offset);
}
if (table_length > QCOW_MAX_SNAPSHOTS_SIZE ||
offset - s->snapshots_offset > INT_MAX)
{
if (!repair) {
ret = -EFBIG;
error_setg(errp, "Snapshot table is too big");
error_append_hint(errp, "You can force-remove all %u "
"overhanging snapshots with qemu-img check "
"-r all\n", s->nb_snapshots - i);
goto fail;
}
fprintf(stderr, "Discarding %u overhanging snapshots (snapshot "
"table is too big)\n", s->nb_snapshots - i);
*nb_clusters_reduced += (s->nb_snapshots - i);
/* Discard current snapshot also */
qcow2_free_single_snapshot(bs, i);
/*
* This leaks all the rest of the snapshot table and the
* snapshots' clusters, but we run in check -r all mode,
* so qcow2_check_refcounts() will take care of it.
*/
s->nb_snapshots = i;
offset = pre_sn_offset;
break;
}
}
@ -135,8 +253,13 @@ fail:
return ret;
}
int qcow2_read_snapshots(BlockDriverState *bs, Error **errp)
{
return qcow2_do_read_snapshots(bs, false, NULL, NULL, errp);
}
/* add at the end of the file a new list of snapshots */
static int qcow2_write_snapshots(BlockDriverState *bs)
int qcow2_write_snapshots(BlockDriverState *bs)
{
BDRVQcow2State *s = bs->opaque;
QCowSnapshot *sn;
@ -156,7 +279,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
sn = s->snapshots + i;
offset = ROUND_UP(offset, 8);
offset += sizeof(h);
offset += sizeof(extra);
offset += MAX(sizeof(extra), sn->extra_data_size);
offset += strlen(sn->id_str);
offset += strlen(sn->name);
@ -203,7 +326,8 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
h.date_sec = cpu_to_be32(sn->date_sec);
h.date_nsec = cpu_to_be32(sn->date_nsec);
h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec);
h.extra_data_size = cpu_to_be32(sizeof(extra));
h.extra_data_size = cpu_to_be32(MAX(sizeof(extra),
sn->extra_data_size));
memset(&extra, 0, sizeof(extra));
extra.vm_state_size_large = cpu_to_be64(sn->vm_state_size);
@ -228,6 +352,22 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
}
offset += sizeof(extra);
if (sn->extra_data_size > sizeof(extra)) {
size_t unknown_extra_data_size =
sn->extra_data_size - sizeof(extra);
/* qcow2_read_snapshots() ensures no unbounded allocation */
assert(unknown_extra_data_size <= BDRV_REQUEST_MAX_BYTES);
assert(sn->unknown_extra_data);
ret = bdrv_pwrite(bs->file, offset, sn->unknown_extra_data,
unknown_extra_data_size);
if (ret < 0) {
goto fail;
}
offset += unknown_extra_data_size;
}
ret = bdrv_pwrite(bs->file, offset, sn->id_str, id_str_size);
if (ret < 0) {
goto fail;
@ -251,7 +391,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
}
QEMU_BUILD_BUG_ON(offsetof(QCowHeader, snapshots_offset) !=
offsetof(QCowHeader, nb_snapshots) + sizeof(header_data.nb_snapshots));
endof(QCowHeader, nb_snapshots));
header_data.nb_snapshots = cpu_to_be32(s->nb_snapshots);
header_data.snapshots_offset = cpu_to_be64(snapshots_offset);
@ -277,6 +417,151 @@ fail:
return ret;
}
int coroutine_fn qcow2_check_read_snapshot_table(BlockDriverState *bs,
BdrvCheckResult *result,
BdrvCheckMode fix)
{
BDRVQcow2State *s = bs->opaque;
Error *local_err = NULL;
int nb_clusters_reduced = 0;
int extra_data_dropped = 0;
int ret;
struct {
uint32_t nb_snapshots;
uint64_t snapshots_offset;
} QEMU_PACKED snapshot_table_pointer;
/* qcow2_do_open() discards this information in check mode */
ret = bdrv_pread(bs->file, offsetof(QCowHeader, nb_snapshots),
&snapshot_table_pointer, sizeof(snapshot_table_pointer));
if (ret < 0) {
result->check_errors++;
fprintf(stderr, "ERROR failed to read the snapshot table pointer from "
"the image header: %s\n", strerror(-ret));
return ret;
}
s->snapshots_offset = be64_to_cpu(snapshot_table_pointer.snapshots_offset);
s->nb_snapshots = be32_to_cpu(snapshot_table_pointer.nb_snapshots);
if (s->nb_snapshots > QCOW_MAX_SNAPSHOTS && (fix & BDRV_FIX_ERRORS)) {
fprintf(stderr, "Discarding %u overhanging snapshots\n",
s->nb_snapshots - QCOW_MAX_SNAPSHOTS);
nb_clusters_reduced += s->nb_snapshots - QCOW_MAX_SNAPSHOTS;
s->nb_snapshots = QCOW_MAX_SNAPSHOTS;
}
ret = qcow2_validate_table(bs, s->snapshots_offset, s->nb_snapshots,
sizeof(QCowSnapshotHeader),
sizeof(QCowSnapshotHeader) * QCOW_MAX_SNAPSHOTS,
"snapshot table", &local_err);
if (ret < 0) {
result->check_errors++;
error_reportf_err(local_err, "ERROR ");
if (s->nb_snapshots > QCOW_MAX_SNAPSHOTS) {
fprintf(stderr, "You can force-remove all %u overhanging snapshots "
"with qemu-img check -r all\n",
s->nb_snapshots - QCOW_MAX_SNAPSHOTS);
}
/* We did not read the snapshot table, so invalidate this information */
s->snapshots_offset = 0;
s->nb_snapshots = 0;
return ret;
}
qemu_co_mutex_unlock(&s->lock);
ret = qcow2_do_read_snapshots(bs, fix & BDRV_FIX_ERRORS,
&nb_clusters_reduced, &extra_data_dropped,
&local_err);
qemu_co_mutex_lock(&s->lock);
if (ret < 0) {
result->check_errors++;
error_reportf_err(local_err,
"ERROR failed to read the snapshot table: ");
/* We did not read the snapshot table, so invalidate this information */
s->snapshots_offset = 0;
s->nb_snapshots = 0;
return ret;
}
result->corruptions += nb_clusters_reduced + extra_data_dropped;
if (nb_clusters_reduced) {
/*
* Update image header now, because:
* (1) qcow2_check_refcounts() relies on s->nb_snapshots to be
* the same as what the image header says,
* (2) this leaks clusters, but qcow2_check_refcounts() will
* fix that.
*/
assert(fix & BDRV_FIX_ERRORS);
snapshot_table_pointer.nb_snapshots = cpu_to_be32(s->nb_snapshots);
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots),
&snapshot_table_pointer.nb_snapshots,
sizeof(snapshot_table_pointer.nb_snapshots));
if (ret < 0) {
result->check_errors++;
fprintf(stderr, "ERROR failed to update the snapshot count in the "
"image header: %s\n", strerror(-ret));
return ret;
}
result->corruptions_fixed += nb_clusters_reduced;
result->corruptions -= nb_clusters_reduced;
}
/*
* All of v3 images' snapshot table entries need to have at least
* 16 bytes of extra data.
*/
if (s->qcow_version >= 3) {
int i;
for (i = 0; i < s->nb_snapshots; i++) {
if (s->snapshots[i].extra_data_size <
sizeof_field(QCowSnapshotExtraData, vm_state_size_large) +
sizeof_field(QCowSnapshotExtraData, disk_size))
{
result->corruptions++;
fprintf(stderr, "%s snapshot table entry %i is incomplete\n",
fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i);
}
}
}
return 0;
}
int coroutine_fn qcow2_check_fix_snapshot_table(BlockDriverState *bs,
BdrvCheckResult *result,
BdrvCheckMode fix)
{
BDRVQcow2State *s = bs->opaque;
int ret;
if (result->corruptions && (fix & BDRV_FIX_ERRORS)) {
qemu_co_mutex_unlock(&s->lock);
ret = qcow2_write_snapshots(bs);
qemu_co_mutex_lock(&s->lock);
if (ret < 0) {
result->check_errors++;
fprintf(stderr, "ERROR failed to update snapshot table: %s\n",
strerror(-ret));
return ret;
}
result->corruptions_fixed += result->corruptions;
result->corruptions = 0;
}
return 0;
}
static void find_new_snapshot_id(BlockDriverState *bs,
char *id_str, int id_str_size)
{
@ -370,6 +655,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
sn->date_sec = sn_info->date_sec;
sn->date_nsec = sn_info->date_nsec;
sn->vm_clock_nsec = sn_info->vm_clock_nsec;
sn->extra_data_size = sizeof(QCowSnapshotExtraData);
/* Allocate the L1 table of the snapshot and copy the current one there. */
l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t));
@ -641,6 +927,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
* The snapshot is now unused, clean up. If we fail after this point, we
* won't recover but just leak clusters.
*/
g_free(sn.unknown_extra_data);
g_free(sn.id_str);
g_free(sn.name);

View file

@ -570,11 +570,47 @@ int qcow2_mark_consistent(BlockDriverState *bs)
return 0;
}
static void qcow2_add_check_result(BdrvCheckResult *out,
const BdrvCheckResult *src,
bool set_allocation_info)
{
out->corruptions += src->corruptions;
out->leaks += src->leaks;
out->check_errors += src->check_errors;
out->corruptions_fixed += src->corruptions_fixed;
out->leaks_fixed += src->leaks_fixed;
if (set_allocation_info) {
out->image_end_offset = src->image_end_offset;
out->bfi = src->bfi;
}
}
static int coroutine_fn qcow2_co_check_locked(BlockDriverState *bs,
BdrvCheckResult *result,
BdrvCheckMode fix)
{
int ret = qcow2_check_refcounts(bs, result, fix);
BdrvCheckResult snapshot_res = {};
BdrvCheckResult refcount_res = {};
int ret;
memset(result, 0, sizeof(*result));
ret = qcow2_check_read_snapshot_table(bs, &snapshot_res, fix);
if (ret < 0) {
qcow2_add_check_result(result, &snapshot_res, false);
return ret;
}
ret = qcow2_check_refcounts(bs, &refcount_res, fix);
qcow2_add_check_result(result, &refcount_res, true);
if (ret < 0) {
qcow2_add_check_result(result, &snapshot_res, false);
return ret;
}
ret = qcow2_check_fix_snapshot_table(bs, &snapshot_res, fix);
qcow2_add_check_result(result, &snapshot_res, false);
if (ret < 0) {
return ret;
}
@ -1410,17 +1446,22 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
goto fail;
}
/* The total size in bytes of the snapshot table is checked in
* qcow2_read_snapshots() because the size of each snapshot is
* variable and we don't know it yet.
* Here we only check the offset and number of snapshots. */
ret = qcow2_validate_table(bs, header.snapshots_offset,
header.nb_snapshots,
sizeof(QCowSnapshotHeader),
sizeof(QCowSnapshotHeader) * QCOW_MAX_SNAPSHOTS,
"Snapshot table", errp);
if (ret < 0) {
goto fail;
if (!(flags & BDRV_O_CHECK)) {
/*
* The total size in bytes of the snapshot table is checked in
* qcow2_read_snapshots() because the size of each snapshot is
* variable and we don't know it yet.
* Here we only check the offset and number of snapshots.
*/
ret = qcow2_validate_table(bs, header.snapshots_offset,
header.nb_snapshots,
sizeof(QCowSnapshotHeader),
sizeof(QCowSnapshotHeader) *
QCOW_MAX_SNAPSHOTS,
"Snapshot table", errp);
if (ret < 0) {
goto fail;
}
}
/* read the level 1 table */
@ -1580,14 +1621,19 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
s->image_backing_file = g_strdup(bs->auto_backing_file);
}
/* Internal snapshots */
s->snapshots_offset = header.snapshots_offset;
s->nb_snapshots = header.nb_snapshots;
/*
* Internal snapshots; skip reading them in check mode, because
* we do not need them then, and we do not want to abort because
* of a broken table.
*/
if (!(flags & BDRV_O_CHECK)) {
s->snapshots_offset = header.snapshots_offset;
s->nb_snapshots = header.nb_snapshots;
ret = qcow2_read_snapshots(bs);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read snapshots");
goto fail;
ret = qcow2_read_snapshots(bs, errp);
if (ret < 0) {
goto fail;
}
}
/* Clear unknown autoclear feature bits */
@ -3028,8 +3074,8 @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset,
if (mode == PREALLOC_MODE_METADATA) {
mode = PREALLOC_MODE_OFF;
}
ret = bdrv_co_truncate(s->data_file, host_offset + cur_bytes, mode,
errp);
ret = bdrv_co_truncate(s->data_file, host_offset + cur_bytes, false,
mode, errp);
if (ret < 0) {
return ret;
}
@ -3345,12 +3391,6 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
}
blk_set_allow_write_beyond_eof(blk, true);
/* Clear the protocol layer and preallocate it if necessary */
ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
if (ret < 0) {
goto out;
}
/* Write the header */
QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
header = g_malloc0(cluster_size);
@ -3449,7 +3489,8 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
}
/* Okay, now that we have a valid image, let's give it the right size */
ret = blk_truncate(blk, qcow2_opts->size, qcow2_opts->preallocation, errp);
ret = blk_truncate(blk, qcow2_opts->size, false, qcow2_opts->preallocation,
errp);
if (ret < 0) {
error_prepend(errp, "Could not resize image: ");
goto out;
@ -3897,7 +3938,8 @@ fail:
}
static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
bool exact, PreallocMode prealloc,
Error **errp)
{
BDRVQcow2State *s = bs->opaque;
uint64_t old_length;
@ -3985,8 +4027,15 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
if ((last_cluster + 1) * s->cluster_size < old_file_size) {
Error *local_err = NULL;
/*
* Do not pass @exact here: It will not help the user if
* we get an error here just because they wanted to shrink
* their qcow2 image (on a block device) with qemu-img.
* (And on the qcow2 layer, the @exact requirement is
* always fulfilled, so there is no need to pass it on.)
*/
bdrv_co_truncate(bs->file, (last_cluster + 1) * s->cluster_size,
PREALLOC_MODE_OFF, &local_err);
false, PREALLOC_MODE_OFF, &local_err);
if (local_err) {
warn_reportf_err(local_err,
"Failed to truncate the tail of the image: ");
@ -4003,7 +4052,12 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
switch (prealloc) {
case PREALLOC_MODE_OFF:
if (has_data_file(bs)) {
ret = bdrv_co_truncate(s->data_file, offset, prealloc, errp);
/*
* If the caller wants an exact resize, the external data
* file should be resized to the exact target size, too,
* so we pass @exact here.
*/
ret = bdrv_co_truncate(s->data_file, offset, exact, prealloc, errp);
if (ret < 0) {
goto fail;
}
@ -4088,7 +4142,8 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
/* Allocate the data area */
new_file_size = allocation_start +
nb_new_data_clusters * s->cluster_size;
ret = bdrv_co_truncate(bs->file, new_file_size, prealloc, errp);
/* Image file grows, so @exact does not matter */
ret = bdrv_co_truncate(bs->file, new_file_size, false, prealloc, errp);
if (ret < 0) {
error_prepend(errp, "Failed to resize underlying file: ");
qcow2_free_clusters(bs, allocation_start,
@ -4191,7 +4246,7 @@ qcow2_co_pwritev_compressed_part(BlockDriverState *bs,
if (len < 0) {
return len;
}
return bdrv_co_truncate(bs->file, len, PREALLOC_MODE_OFF, NULL);
return bdrv_co_truncate(bs->file, len, false, PREALLOC_MODE_OFF, NULL);
}
if (offset_into_cluster(s, offset)) {
@ -4428,7 +4483,7 @@ static int make_completely_empty(BlockDriverState *bs)
goto fail;
}
ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size,
ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size, false,
PREALLOC_MODE_OFF, &local_err);
if (ret < 0) {
error_report_err(local_err);
@ -4913,12 +4968,74 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
return 0;
}
/*
* Upgrades an image's version. While newer versions encompass all
* features of older versions, some things may have to be presented
* differently.
*/
static int qcow2_upgrade(BlockDriverState *bs, int target_version,
BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
Error **errp)
{
BDRVQcow2State *s = bs->opaque;
bool need_snapshot_update;
int current_version = s->qcow_version;
int i;
int ret;
/* This is qcow2_upgrade(), not qcow2_downgrade() */
assert(target_version > current_version);
/* There are no other versions (yet) that you can upgrade to */
assert(target_version == 3);
status_cb(bs, 0, 2, cb_opaque);
/*
* In v2, snapshots do not need to have extra data. v3 requires
* the 64-bit VM state size and the virtual disk size to be
* present.
* qcow2_write_snapshots() will always write the list in the
* v3-compliant format.
*/
need_snapshot_update = false;
for (i = 0; i < s->nb_snapshots; i++) {
if (s->snapshots[i].extra_data_size <
sizeof_field(QCowSnapshotExtraData, vm_state_size_large) +
sizeof_field(QCowSnapshotExtraData, disk_size))
{
need_snapshot_update = true;
break;
}
}
if (need_snapshot_update) {
ret = qcow2_write_snapshots(bs);
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to update the snapshot table");
return ret;
}
}
status_cb(bs, 1, 2, cb_opaque);
s->qcow_version = target_version;
ret = qcow2_update_header(bs);
if (ret < 0) {
s->qcow_version = current_version;
error_setg_errno(errp, -ret, "Failed to update the image header");
return ret;
}
status_cb(bs, 2, 2, cb_opaque);
return 0;
}
typedef enum Qcow2AmendOperation {
/* This is the value Qcow2AmendHelperCBInfo::last_operation will be
* statically initialized to so that the helper CB can discern the first
* invocation from an operation change */
QCOW2_NO_OPERATION = 0,
QCOW2_UPGRADING,
QCOW2_CHANGING_REFCOUNT_ORDER,
QCOW2_DOWNGRADING,
} Qcow2AmendOperation;
@ -5101,17 +5218,16 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
helper_cb_info = (Qcow2AmendHelperCBInfo){
.original_status_cb = status_cb,
.original_cb_opaque = cb_opaque,
.total_operations = (new_version < old_version)
.total_operations = (new_version != old_version)
+ (s->refcount_bits != refcount_bits)
};
/* Upgrade first (some features may require compat=1.1) */
if (new_version > old_version) {
s->qcow_version = new_version;
ret = qcow2_update_header(bs);
helper_cb_info.current_operation = QCOW2_UPGRADING;
ret = qcow2_upgrade(bs, new_version, &qcow2_amend_helper_cb,
&helper_cb_info, errp);
if (ret < 0) {
s->qcow_version = old_version;
error_setg_errno(errp, -ret, "Failed to update the image header");
return ret;
}
}
@ -5207,7 +5323,11 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
return ret;
}
ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, errp);
/*
* Amending image options should ensure that the image has
* exactly the given new values, so pass exact=true here.
*/
ret = blk_truncate(blk, new_size, true, PREALLOC_MODE_OFF, errp);
blk_unref(blk);
if (ret < 0) {
return ret;

View file

@ -61,6 +61,9 @@
* space for snapshot names and IDs */
#define QCOW_MAX_SNAPSHOTS_SIZE (1024 * QCOW_MAX_SNAPSHOTS)
/* Maximum amount of extra data per snapshot table entry to accept */
#define QCOW_MAX_SNAPSHOT_EXTRA_DATA 1024
/* Bitmap header extension constraints */
#define QCOW2_MAX_BITMAPS 65535
#define QCOW2_MAX_BITMAP_DIRECTORY_SIZE (1024 * QCOW2_MAX_BITMAPS)
@ -181,6 +184,10 @@ typedef struct QCowSnapshot {
uint32_t date_sec;
uint32_t date_nsec;
uint64_t vm_clock_nsec;
/* Size of all extra data, including QCowSnapshotExtraData if available */
uint32_t extra_data_size;
/* Data beyond QCowSnapshotExtraData, if any */
void *unknown_extra_data;
} QCowSnapshot;
struct Qcow2Cache;
@ -708,7 +715,15 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
Error **errp);
void qcow2_free_snapshots(BlockDriverState *bs);
int qcow2_read_snapshots(BlockDriverState *bs);
int qcow2_read_snapshots(BlockDriverState *bs, Error **errp);
int qcow2_write_snapshots(BlockDriverState *bs);
int coroutine_fn qcow2_check_read_snapshot_table(BlockDriverState *bs,
BdrvCheckResult *result,
BdrvCheckMode fix);
int coroutine_fn qcow2_check_fix_snapshot_table(BlockDriverState *bs,
BdrvCheckResult *result,
BdrvCheckMode fix);
/* qcow2-cache.c functions */
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,

View file

@ -673,8 +673,11 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
l1_size = header.cluster_size * header.table_size;
/* File must start empty and grow, check truncate is supported */
ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
/*
* The QED format associates file length with allocation status,
* so a new file (which is empty) must have a length of 0.
*/
ret = blk_truncate(blk, 0, true, PREALLOC_MODE_OFF, errp);
if (ret < 0) {
goto out;
}
@ -1461,6 +1464,7 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs,
static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs,
int64_t offset,
bool exact,
PreallocMode prealloc,
Error **errp)
{

View file

@ -370,7 +370,8 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
}
static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
bool exact, PreallocMode prealloc,
Error **errp)
{
BDRVRawState *s = bs->opaque;
@ -386,7 +387,7 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
s->size = offset;
offset += s->offset;
return bdrv_co_truncate(bs->file, offset, prealloc, errp);
return bdrv_co_truncate(bs->file, offset, exact, prealloc, errp);
}
static void raw_eject(BlockDriverState *bs, bool eject_flag)

View file

@ -1087,6 +1087,7 @@ static int64_t qemu_rbd_getlength(BlockDriverState *bs)
static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs,
int64_t offset,
bool exact,
PreallocMode prealloc,
Error **errp)
{

View file

@ -2285,7 +2285,8 @@ static int64_t sd_getlength(BlockDriverState *bs)
}
static int coroutine_fn sd_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
bool exact, PreallocMode prealloc,
Error **errp)
{
BDRVSheepdogState *s = bs->opaque;
int ret, fd;
@ -2601,7 +2602,7 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
assert(!flags);
if (offset > s->inode.vdi_size) {
ret = sd_co_truncate(bs, offset, PREALLOC_MODE_OFF, NULL);
ret = sd_co_truncate(bs, offset, false, PREALLOC_MODE_OFF, NULL);
if (ret < 0) {
return ret;
}

View file

@ -1295,7 +1295,8 @@ static int64_t ssh_getlength(BlockDriverState *bs)
}
static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
bool exact, PreallocMode prealloc,
Error **errp)
{
BDRVSSHState *s = bs->opaque;

View file

@ -45,9 +45,9 @@ backup_do_cow_return(void *job, int64_t offset, uint64_t bytes, int ret) "job %p
block_copy_skip(void *bcs, int64_t start) "bcs %p start %"PRId64
block_copy_skip_range(void *bcs, int64_t start, uint64_t bytes) "bcs %p start %"PRId64" bytes %"PRId64
block_copy_process(void *bcs, int64_t start) "bcs %p start %"PRId64
block_copy_with_bounce_buffer_read_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
block_copy_with_bounce_buffer_write_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
block_copy_with_offload_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
block_copy_copy_range_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
block_copy_read_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
block_copy_write_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
# ../blockdev.c
qmp_block_job_cancel(void *job) "job %p"
@ -152,9 +152,12 @@ nvme_submit_command_raw(int c0, int c1, int c2, int c3, int c4, int c5, int c6,
nvme_handle_event(void *s) "s %p"
nvme_poll_cb(void *s) "s %p"
nvme_prw_aligned(void *s, int is_write, uint64_t offset, uint64_t bytes, int flags, int niov) "s %p is_write %d offset %"PRId64" bytes %"PRId64" flags %d niov %d"
nvme_write_zeroes(void *s, uint64_t offset, uint64_t bytes, int flags) "s %p offset %"PRId64" bytes %"PRId64" flags %d"
nvme_qiov_unaligned(const void *qiov, int n, void *base, size_t size, int align) "qiov %p n %d base %p size 0x%zx align 0x%x"
nvme_prw_buffered(void *s, uint64_t offset, uint64_t bytes, int niov, int is_write) "s %p offset %"PRId64" bytes %"PRId64" niov %d is_write %d"
nvme_rw_done(void *s, int is_write, uint64_t offset, uint64_t bytes, int ret) "s %p is_write %d offset %"PRId64" bytes %"PRId64" ret %d"
nvme_dsm(void *s, uint64_t offset, uint64_t bytes) "s %p offset %"PRId64" bytes %"PRId64""
nvme_dsm_done(void *s, uint64_t offset, uint64_t bytes, int ret) "s %p offset %"PRId64" bytes %"PRId64" ret %d"
nvme_dma_map_flush(void *s) "s %p"
nvme_free_req_queue_wait(void *q) "q %p"
nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p cmd %p req %p qiov %p entries %d"

View file

@ -874,7 +874,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
}
if (image_type == VDI_TYPE_STATIC) {
ret = blk_truncate(blk, offset + blocks * block_size,
ret = blk_truncate(blk, offset + blocks * block_size, false,
PREALLOC_MODE_OFF, errp);
if (ret < 0) {
error_prepend(errp, "Failed to statically allocate file");

View file

@ -557,8 +557,8 @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s,
ret = -EINVAL;
goto exit;
}
ret = bdrv_truncate(bs->file, new_file_size, PREALLOC_MODE_OFF,
NULL);
ret = bdrv_truncate(bs->file, new_file_size, false,
PREALLOC_MODE_OFF, NULL);
if (ret < 0) {
goto exit;
}

View file

@ -1263,7 +1263,7 @@ static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s,
return -EINVAL;
}
return bdrv_truncate(bs->file, *new_offset + s->block_size,
return bdrv_truncate(bs->file, *new_offset + s->block_size, false,
PREALLOC_MODE_OFF, NULL);
}
@ -1702,12 +1702,13 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s,
if (type == VHDX_TYPE_DYNAMIC) {
/* All zeroes, so we can just extend the file - the end of the BAT
* is the furthest thing we have written yet */
ret = blk_truncate(blk, data_file_offset, PREALLOC_MODE_OFF, errp);
ret = blk_truncate(blk, data_file_offset, false, PREALLOC_MODE_OFF,
errp);
if (ret < 0) {
goto exit;
}
} else if (type == VHDX_TYPE_FIXED) {
ret = blk_truncate(blk, data_file_offset + image_size,
ret = blk_truncate(blk, data_file_offset + image_size, false,
PREALLOC_MODE_OFF, errp);
if (ret < 0) {
goto exit;

View file

@ -2076,7 +2076,7 @@ vmdk_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
return length;
}
length = QEMU_ALIGN_UP(length, BDRV_SECTOR_SIZE);
ret = bdrv_truncate(s->extents[i].file, length,
ret = bdrv_truncate(s->extents[i].file, length, false,
PREALLOC_MODE_OFF, NULL);
if (ret < 0) {
return ret;
@ -2118,7 +2118,7 @@ static int vmdk_init_extent(BlockBackend *blk,
int gd_buf_size;
if (flat) {
ret = blk_truncate(blk, filesize, PREALLOC_MODE_OFF, errp);
ret = blk_truncate(blk, filesize, false, PREALLOC_MODE_OFF, errp);
goto exit;
}
magic = cpu_to_be32(VMDK4_MAGIC);
@ -2181,7 +2181,7 @@ static int vmdk_init_extent(BlockBackend *blk,
goto exit;
}
ret = blk_truncate(blk, le64_to_cpu(header.grain_offset) << 9,
ret = blk_truncate(blk, le64_to_cpu(header.grain_offset) << 9, false,
PREALLOC_MODE_OFF, errp);
if (ret < 0) {
goto exit;
@ -2523,7 +2523,7 @@ static int coroutine_fn vmdk_co_do_create(int64_t size,
/* bdrv_pwrite write padding zeros to align to sector, we don't need that
* for description file */
if (desc_offset == 0) {
ret = blk_truncate(blk, desc_len, PREALLOC_MODE_OFF, errp);
ret = blk_truncate(blk, desc_len, false, PREALLOC_MODE_OFF, errp);
if (ret < 0) {
goto exit;
}

View file

@ -898,7 +898,7 @@ static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
/* Add footer to total size */
total_size += HEADER_SIZE;
ret = blk_truncate(blk, total_size, PREALLOC_MODE_OFF, errp);
ret = blk_truncate(blk, total_size, false, PREALLOC_MODE_OFF, errp);
if (ret < 0) {
return ret;
}

View file

@ -3204,7 +3204,7 @@ void qmp_block_resize(bool has_device, const char *device,
}
bdrv_drained_begin(bs);
ret = blk_truncate(blk, size, PREALLOC_MODE_OFF, errp);
ret = blk_truncate(blk, size, false, PREALLOC_MODE_OFF, errp);
bdrv_drained_end(bs);
out:

View file

@ -42,9 +42,9 @@
*/
static VirtIOFeature feature_sizes[] = {
{.flags = 1ULL << VIRTIO_BLK_F_DISCARD,
.end = virtio_endof(struct virtio_blk_config, discard_sector_alignment)},
.end = endof(struct virtio_blk_config, discard_sector_alignment)},
{.flags = 1ULL << VIRTIO_BLK_F_WRITE_ZEROES,
.end = virtio_endof(struct virtio_blk_config, write_zeroes_may_unmap)},
.end = endof(struct virtio_blk_config, write_zeroes_may_unmap)},
{}
};

View file

@ -90,15 +90,15 @@ static inline __virtio16 *virtio_net_rsc_ext_num_dupacks(
static VirtIOFeature feature_sizes[] = {
{.flags = 1ULL << VIRTIO_NET_F_MAC,
.end = virtio_endof(struct virtio_net_config, mac)},
.end = endof(struct virtio_net_config, mac)},
{.flags = 1ULL << VIRTIO_NET_F_STATUS,
.end = virtio_endof(struct virtio_net_config, status)},
.end = endof(struct virtio_net_config, status)},
{.flags = 1ULL << VIRTIO_NET_F_MQ,
.end = virtio_endof(struct virtio_net_config, max_virtqueue_pairs)},
.end = endof(struct virtio_net_config, max_virtqueue_pairs)},
{.flags = 1ULL << VIRTIO_NET_F_MTU,
.end = virtio_endof(struct virtio_net_config, mtu)},
.end = endof(struct virtio_net_config, mtu)},
{.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX,
.end = virtio_endof(struct virtio_net_config, duplex)},
.end = endof(struct virtio_net_config, duplex)},
{}
};

View file

@ -16,6 +16,7 @@
#define BLOCK_COPY_H
#include "block/block.h"
#include "qemu/co-shared-resource.h"
typedef struct BlockCopyInFlightReq {
int64_t start_byte;
@ -37,7 +38,7 @@ typedef struct BlockCopyState {
BdrvDirtyBitmap *copy_bitmap;
int64_t cluster_size;
bool use_copy_range;
int64_t copy_range_size;
int64_t copy_size;
uint64_t len;
QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs;
@ -69,6 +70,8 @@ typedef struct BlockCopyState {
*/
ProgressResetCallbackFunc progress_reset_callback;
void *progress_opaque;
SharedResource *mem;
} BlockCopyState;
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,

View file

@ -346,10 +346,10 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
const char *backing_file);
void bdrv_refresh_filename(BlockDriverState *bs);
int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset,
int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
PreallocMode prealloc, Error **errp);
int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
Error **errp);
int bdrv_truncate(BdrvChild *child, int64_t offset, bool exact,
PreallocMode prealloc, Error **errp);
int64_t bdrv_nb_sectors(BlockDriverState *bs);
int64_t bdrv_getlength(BlockDriverState *bs);

View file

@ -334,8 +334,23 @@ struct BlockDriver {
* bdrv_parse_filename.
*/
const char *protocol_name;
/*
* Truncate @bs to @offset bytes using the given @prealloc mode
* when growing. Modes other than PREALLOC_MODE_OFF should be
* rejected when shrinking @bs.
*
* If @exact is true, @bs must be resized to exactly @offset.
* Otherwise, it is sufficient for @bs (if it is a host block
* device and thus there is no way to resize it) to be at least
* @offset bytes in length.
*
* If @exact is true and this function fails but would succeed
* with @exact = false, it should return -ENOTSUP.
*/
int coroutine_fn (*bdrv_co_truncate)(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp);
bool exact, PreallocMode prealloc,
Error **errp);
int64_t (*bdrv_getlength)(BlockDriverState *bs);
bool has_variable_length;

View file

@ -653,12 +653,29 @@ typedef struct NvmeIdNs {
uint8_t mc;
uint8_t dpc;
uint8_t dps;
uint8_t res30[98];
uint8_t nmic;
uint8_t rescap;
uint8_t fpi;
uint8_t dlfeat;
uint8_t res34[94];
NvmeLBAF lbaf[16];
uint8_t res192[192];
uint8_t vs[3712];
} NvmeIdNs;
/*Deallocate Logical Block Features*/
#define NVME_ID_NS_DLFEAT_GUARD_CRC(dlfeat) ((dlfeat) & 0x10)
#define NVME_ID_NS_DLFEAT_WRITE_ZEROES(dlfeat) ((dlfeat) & 0x08)
#define NVME_ID_NS_DLFEAT_READ_BEHAVIOR(dlfeat) ((dlfeat) & 0x7)
#define NVME_ID_NS_DLFEAT_READ_BEHAVIOR_UNDEFINED 0
#define NVME_ID_NS_DLFEAT_READ_BEHAVIOR_ZEROES 1
#define NVME_ID_NS_DLFEAT_READ_BEHAVIOR_ONES 2
#define NVME_ID_NS_NSFEAT_THIN(nsfeat) ((nsfeat & 0x1))
#define NVME_ID_NS_FLBAS_EXTENDED(flbas) ((flbas >> 4) & 0x1)
#define NVME_ID_NS_FLBAS_INDEX(flbas) ((flbas & 0xf))

View file

@ -37,13 +37,6 @@ static inline hwaddr vring_align(hwaddr addr,
return QEMU_ALIGN_UP(addr, align);
}
/*
* Calculate the number of bytes up to and including the given 'field' of
* 'container'.
*/
#define virtio_endof(container, field) \
(offsetof(container, field) + sizeof_field(container, field))
typedef struct VirtIOFeature {
uint64_t flags;
size_t end;

View file

@ -0,0 +1,71 @@
/*
* Helper functionality for distributing a fixed total amount of
* an abstract resource among multiple coroutines.
*
* Copyright (c) 2019 Virtuozzo International GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef QEMU_CO_SHARED_RESOURCE_H
#define QEMU_CO_SHARED_RESOURCE_H
typedef struct SharedResource SharedResource;
/*
* Create SharedResource structure
*
* @total: total amount of some resource to be shared between clients
*
* Note: this API is not thread-safe.
*/
SharedResource *shres_create(uint64_t total);
/*
* Release SharedResource structure
*
* This function may only be called once everything allocated by all
* clients has been deallocated.
*/
void shres_destroy(SharedResource *s);
/*
* Try to allocate an amount of @n. Return true on success, and false
* if there is too little left of the collective resource to fulfill
* the request.
*/
bool co_try_get_from_shres(SharedResource *s, uint64_t n);
/*
* Allocate an amount of @n, and, if necessary, yield until
* that becomes possible.
*/
void coroutine_fn co_get_from_shres(SharedResource *s, uint64_t n);
/*
* Deallocate an amount of @n. The total amount allocated by a caller
* does not need to be deallocated/released with a single call, but may
* be split over several calls. For example, get(4), get(3), and then
* put(5), put(2).
*/
void coroutine_fn co_put_to_shres(SharedResource *s, uint64_t n);
#endif /* QEMU_CO_SHARED_RESOURCE_H */

View file

@ -60,6 +60,13 @@
#define sizeof_field(type, field) sizeof(((type *)0)->field)
/*
* Calculate the number of bytes up to and including the given 'field' of
* 'container'.
*/
#define endof(container, field) \
(offsetof(container, field) + sizeof_field(container, field))
/* Convert from a base type to a parent type, with compile time checking. */
#ifdef __GNUC__
#define DO_UPCAST(type, field, dev) ( __extension__ ( { \

View file

@ -121,6 +121,10 @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, void *opaque);
int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags);
int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset,
unsigned int bytes,
QEMUIOVector *qiov, size_t qiov_offset,
BdrvRequestFlags flags);
int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags);
@ -233,8 +237,8 @@ int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset,
int bytes, BdrvRequestFlags flags);
int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf,
int bytes);
int blk_truncate(BlockBackend *blk, int64_t offset, PreallocMode prealloc,
Error **errp);
int blk_truncate(BlockBackend *blk, int64_t offset, bool exact,
PreallocMode prealloc, Error **errp);
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes);
int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,
int64_t pos, int size);

View file

@ -71,7 +71,7 @@ class QEMUMachine(object):
def __init__(self, binary, args=None, wrapper=None, name=None,
test_dir="/var/tmp", monitor_address=None,
socket_scm_helper=None):
socket_scm_helper=None, sock_dir=None):
'''
Initialize a QEMUMachine
@ -90,6 +90,8 @@ def __init__(self, binary, args=None, wrapper=None, name=None,
wrapper = []
if name is None:
name = "qemu-%d" % os.getpid()
if sock_dir is None:
sock_dir = test_dir
self._name = name
self._monitor_address = monitor_address
self._vm_monitor = None
@ -106,12 +108,14 @@ def __init__(self, binary, args=None, wrapper=None, name=None,
self._qemu_full_args = None
self._test_dir = test_dir
self._temp_dir = None
self._sock_dir = sock_dir
self._launched = False
self._machine = None
self._console_set = False
self._console_device_type = None
self._console_address = None
self._console_socket = None
self._remove_files = []
# just in case logging wasn't configured by the main script:
logging.basicConfig()
@ -236,8 +240,9 @@ def _base_args(self):
if self._machine is not None:
args.extend(['-machine', self._machine])
if self._console_set:
self._console_address = os.path.join(self._temp_dir,
self._console_address = os.path.join(self._sock_dir,
self._name + "-console.sock")
self._remove_files.append(self._console_address)
chardev = ('socket,id=console,path=%s,server,nowait' %
self._console_address)
args.extend(['-chardev', chardev])
@ -253,8 +258,9 @@ def _pre_launch(self):
if self._monitor_address is not None:
self._vm_monitor = self._monitor_address
else:
self._vm_monitor = os.path.join(self._temp_dir,
self._vm_monitor = os.path.join(self._sock_dir,
self._name + "-monitor.sock")
self._remove_files.append(self._vm_monitor)
self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log")
self._qemu_log_file = open(self._qemu_log_path, 'wb')
@ -279,6 +285,9 @@ def _post_shutdown(self):
shutil.rmtree(self._temp_dir)
self._temp_dir = None
while len(self._remove_files) > 0:
self._remove_if_exists(self._remove_files.pop())
def launch(self):
"""
Launch the VM and make sure we cleanup and expose the

View file

@ -84,14 +84,17 @@ class QEMUQtestMachine(QEMUMachine):
'''A QEMU VM'''
def __init__(self, binary, args=None, name=None, test_dir="/var/tmp",
socket_scm_helper=None):
socket_scm_helper=None, sock_dir=None):
if name is None:
name = "qemu-%d" % os.getpid()
if sock_dir is None:
sock_dir = test_dir
super(QEMUQtestMachine,
self).__init__(binary, args, name=name, test_dir=test_dir,
socket_scm_helper=socket_scm_helper)
socket_scm_helper=socket_scm_helper,
sock_dir=sock_dir)
self._qtest = None
self._qtest_path = os.path.join(test_dir, name + "-qtest.sock")
self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock")
def _base_args(self):
args = super(QEMUQtestMachine, self)._base_args()

View file

@ -3656,7 +3656,7 @@ static int img_resize(int argc, char **argv)
Error *err = NULL;
int c, ret, relative;
const char *filename, *fmt, *size;
int64_t n, total_size, current_size, new_size;
int64_t n, total_size, current_size;
bool quiet = false;
BlockBackend *blk = NULL;
PreallocMode prealloc = PREALLOC_MODE_OFF;
@ -3831,43 +3831,17 @@ static int img_resize(int argc, char **argv)
}
}
ret = blk_truncate(blk, total_size, prealloc, &err);
if (ret < 0) {
/*
* The user expects the image to have the desired size after
* resizing, so pass @exact=true. It is of no use to report
* success when the image has not actually been resized.
*/
ret = blk_truncate(blk, total_size, true, prealloc, &err);
if (!ret) {
qprintf(quiet, "Image resized.\n");
} else {
error_report_err(err);
goto out;
}
new_size = blk_getlength(blk);
if (new_size < 0) {
error_report("Failed to verify truncated image length: %s",
strerror(-new_size));
ret = -1;
goto out;
}
/* Some block drivers implement a truncation method, but only so
* the user can cause qemu to refresh the image's size from disk.
* The idea is that the user resizes the image outside of qemu and
* then invokes block_resize to inform qemu about it.
* (This includes iscsi and file-posix for device files.)
* Of course, that is not the behavior someone invoking
* qemu-img resize would find useful, so we catch that behavior
* here and tell the user. */
if (new_size != total_size && new_size == current_size) {
error_report("Image was not resized; resizing may not be supported "
"for this image");
ret = -1;
goto out;
}
if (new_size != total_size) {
warn_report("Image should have been resized to %" PRIi64
" bytes, but was resized to %" PRIi64 " bytes",
total_size, new_size);
}
qprintf(quiet, "Image resized.\n");
out:
blk_unref(blk);
if (ret) {

View file

@ -1710,7 +1710,12 @@ static int truncate_f(BlockBackend *blk, int argc, char **argv)
return offset;
}
ret = blk_truncate(blk, offset, PREALLOC_MODE_OFF, &local_err);
/*
* qemu-io is a debugging tool, so let us be strict here and pass
* exact=true. It is better to err on the "emit more errors" side
* than to be overly permissive.
*/
ret = blk_truncate(blk, offset, true, PREALLOC_MODE_OFF, &local_err);
if (ret < 0) {
error_report_err(local_err);
return ret;

View file

@ -28,7 +28,7 @@ status=1 # failure is the default!
_cleanup()
{
rm -f nbd.sock
rm -f "$SOCK_DIR/nbd.sock"
rm -f nbd-fault-injector.out
rm -f nbd-fault-injector.conf
}
@ -80,10 +80,10 @@ EOF
if [ "$proto" = "tcp" ]; then
nbd_addr="127.0.0.1:0"
else
nbd_addr="$TEST_DIR/nbd.sock"
nbd_addr="$SOCK_DIR/nbd.sock"
fi
rm -f "$TEST_DIR/nbd.sock"
rm -f "$SOCK_DIR/nbd.sock"
echo > "$TEST_DIR/nbd-fault-injector.out"
$PYTHON nbd-fault-injector.py $extra_args "$nbd_addr" "$TEST_DIR/nbd-fault-injector.conf" >"$TEST_DIR/nbd-fault-injector.out" 2>&1 &

View file

@ -110,43 +110,43 @@ read failed: Input/output error
=== Check disconnect before neg1 ===
qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock
=== Check disconnect after neg1 ===
qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock
=== Check disconnect 8 neg1 ===
qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock
=== Check disconnect 16 neg1 ===
qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock
=== Check disconnect before export ===
qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock
=== Check disconnect after export ===
qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock
=== Check disconnect 4 export ===
qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock
=== Check disconnect 12 export ===
qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock
=== Check disconnect 16 export ===
qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock
=== Check disconnect before neg2 ===
qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock
=== Check disconnect after neg2 ===
@ -154,11 +154,11 @@ read failed: Input/output error
=== Check disconnect 8 neg2 ===
qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock
=== Check disconnect 10 neg2 ===
qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock
=== Check disconnect before request ===
@ -195,23 +195,23 @@ read 512/512 bytes at offset 0
=== Check disconnect before neg-classic ===
qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///?socket=SOCK_DIR/nbd.sock
=== Check disconnect 8 neg-classic ===
qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///?socket=SOCK_DIR/nbd.sock
=== Check disconnect 16 neg-classic ===
qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///?socket=SOCK_DIR/nbd.sock
=== Check disconnect 24 neg-classic ===
qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///?socket=SOCK_DIR/nbd.sock
=== Check disconnect 28 neg-classic ===
qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
qemu-io: can't open device nbd+unix:///?socket=SOCK_DIR/nbd.sock
=== Check disconnect after neg-classic ===

View file

@ -24,7 +24,7 @@ import iotests
nsec_per_sec = 1000000000
class ThrottleTestCase(iotests.QMPTestCase):
test_img = "null-aio://"
test_driver = "null-aio"
max_drives = 3
def blockstats(self, device):
@ -35,10 +35,14 @@ class ThrottleTestCase(iotests.QMPTestCase):
return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
raise Exception("Device not found for blockstats: %s" % device)
def required_drivers(self):
return [self.test_driver]
@iotests.skip_if_unsupported(required_drivers)
def setUp(self):
self.vm = iotests.VM()
for i in range(0, self.max_drives):
self.vm.add_drive(self.test_img, "file.read-zeroes=on")
self.vm.add_drive(self.test_driver + "://", "file.read-zeroes=on")
self.vm.launch()
def tearDown(self):
@ -264,16 +268,15 @@ class ThrottleTestCase(iotests.QMPTestCase):
self.assertEqual(self.blockstats('drive1')[0], 4096)
class ThrottleTestCoroutine(ThrottleTestCase):
test_img = "null-co://"
test_driver = "null-co"
class ThrottleTestGroupNames(iotests.QMPTestCase):
test_img = "null-aio://"
max_drives = 3
def setUp(self):
self.vm = iotests.VM()
for i in range(0, self.max_drives):
self.vm.add_drive(self.test_img,
self.vm.add_drive("null-co://",
"throttling.iops-total=100,file.read-zeroes=on")
self.vm.launch()
@ -376,10 +379,10 @@ class ThrottleTestRemovableMedia(iotests.QMPTestCase):
def test_removable_media(self):
# Add a couple of dummy nodes named cd0 and cd1
result = self.vm.qmp("blockdev-add", driver="null-aio",
result = self.vm.qmp("blockdev-add", driver="null-co",
read_zeroes=True, node_name="cd0")
self.assert_qmp(result, 'return', {})
result = self.vm.qmp("blockdev-add", driver="null-aio",
result = self.vm.qmp("blockdev-add", driver="null-co",
read_zeroes=True, node_name="cd1")
self.assert_qmp(result, 'return', {})
@ -426,4 +429,6 @@ class ThrottleTestRemovableMedia(iotests.QMPTestCase):
if __name__ == '__main__':
if 'null-co' not in iotests.supported_formats():
iotests.notrun('null-co driver support missing')
iotests.main(supported_fmts=["raw"])

View file

@ -30,7 +30,7 @@ bad_offset = bad_sector * 512
blkdebug_file = os.path.join(iotests.test_dir, 'blkdebug.conf')
class BlockDeviceStatsTestCase(iotests.QMPTestCase):
test_img = "null-aio://"
test_driver = "null-aio"
total_rd_bytes = 0
total_rd_ops = 0
total_wr_bytes = 0
@ -67,6 +67,10 @@ sector = "%d"
''' % (bad_sector, bad_sector))
file.close()
def required_drivers(self):
return [self.test_driver]
@iotests.skip_if_unsupported(required_drivers)
def setUp(self):
drive_args = []
drive_args.append("stats-intervals.0=%d" % interval_length)
@ -76,8 +80,8 @@ sector = "%d"
(self.account_failed and "on" or "off"))
drive_args.append("file.image.read-zeroes=on")
self.create_blkdebug_file()
self.vm = iotests.VM().add_drive('blkdebug:%s:%s' %
(blkdebug_file, self.test_img),
self.vm = iotests.VM().add_drive('blkdebug:%s:%s://' %
(blkdebug_file, self.test_driver),
','.join(drive_args))
self.vm.launch()
# Set an initial value for the clock
@ -337,7 +341,9 @@ class BlockDeviceStatsTestAccountBoth(BlockDeviceStatsTestCase):
account_failed = True
class BlockDeviceStatsTestCoroutine(BlockDeviceStatsTestCase):
test_img = "null-co://"
test_driver = "null-co"
if __name__ == '__main__':
if 'null-co' not in iotests.supported_formats():
iotests.notrun('null-co driver support missing')
iotests.main(supported_fmts=["raw"])

View file

@ -34,7 +34,7 @@ _cleanup()
{
_cleanup_qemu
_cleanup_test_img
rm -f "$TEST_DIR/nbd"
rm -f "$SOCK_DIR/nbd"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@ -69,7 +69,7 @@ _send_qemu_cmd $QEMU_HANDLE \
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'nbd-server-start',
'arguments': { 'addr': { 'type': 'unix',
'data': { 'path': '$TEST_DIR/nbd' }}}}" \
'data': { 'path': '$SOCK_DIR/nbd' }}}}" \
'return'
_send_qemu_cmd $QEMU_HANDLE \
@ -78,7 +78,7 @@ _send_qemu_cmd $QEMU_HANDLE \
'return'
$QEMU_IO_PROG -f raw -r -c 'read -P 42 0 64k' \
"nbd+unix:///drv?socket=$TEST_DIR/nbd" 2>&1 \
"nbd+unix:///drv?socket=$SOCK_DIR/nbd" 2>&1 \
| _filter_qemu_io | _filter_nbd
_send_qemu_cmd $QEMU_HANDLE \
@ -87,7 +87,7 @@ _send_qemu_cmd $QEMU_HANDLE \
'return'
$QEMU_IO_PROG -f raw -r -c close \
"nbd+unix:///drv?socket=$TEST_DIR/nbd" 2>&1 \
"nbd+unix:///drv?socket=$SOCK_DIR/nbd" 2>&1 \
| _filter_qemu_io | _filter_nbd
_send_qemu_cmd $QEMU_HANDLE \

View file

@ -8,7 +8,7 @@ wrote 65536/65536 bytes at offset 0
read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": {}}
qemu-io: can't open device nbd+unix:///drv?socket=TEST_DIR/nbd: Requested export not available
qemu-io: can't open device nbd+unix:///drv?socket=SOCK_DIR/nbd: Requested export not available
server reported: export 'drv' not present
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}

View file

@ -29,7 +29,7 @@ status=1 # failure is the default!
_cleanup()
{
_cleanup_qemu
rm -f "$TEST_DIR/nbd"
rm -f "$SOCK_DIR/nbd"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@ -51,12 +51,12 @@ _send_qemu_cmd $QEMU_HANDLE \
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'nbd-server-start',
'arguments': { 'addr': { 'type': 'unix',
'data': { 'path': '$TEST_DIR/nbd' }}}}" \
'data': { 'path': '$SOCK_DIR/nbd' }}}}" \
'return'
# This should just result in a client error, not in the server crashing
$QEMU_IO_PROG -f raw -c quit \
"nbd+unix:///no_such_export?socket=$TEST_DIR/nbd" 2>&1 \
"nbd+unix:///no_such_export?socket=$SOCK_DIR/nbd" 2>&1 \
| _filter_qemu_io | _filter_nbd
_send_qemu_cmd $QEMU_HANDLE \

View file

@ -1,7 +1,7 @@
QA output created by 143
{"return": {}}
{"return": {}}
qemu-io: can't open device nbd+unix:///no_such_export?socket=TEST_DIR/nbd: Requested export not available
qemu-io: can't open device nbd+unix:///no_such_export?socket=SOCK_DIR/nbd: Requested export not available
server reported: export 'no_such_export' not present
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}

View file

@ -32,7 +32,7 @@ NBD_IPV6_PORT_START = NBD_PORT_END
NBD_IPV6_PORT_END = NBD_IPV6_PORT_START + 1024
test_img = os.path.join(iotests.test_dir, 'test.img')
unix_socket = os.path.join(iotests.test_dir, 'nbd.socket')
unix_socket = os.path.join(iotests.sock_dir, 'nbd.socket')
def flatten_sock_addr(crumpled_address):

View file

@ -26,7 +26,7 @@ echo "QA output created by $seq"
status=1 # failure is the default!
MIG_SOCKET="${TEST_DIR}/migrate"
MIG_SOCKET="${SOCK_DIR}/migrate"
_cleanup()
{

View file

@ -31,7 +31,7 @@ _cleanup()
{
_cleanup_test_img
rm -f "$TEST_IMG.overlay"
rm -f "$TEST_DIR/nbd.socket"
rm -f "$SOCK_DIR/nbd.socket"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@ -133,7 +133,7 @@ success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \
'addr': {
'type': 'unix',
'data': {
'path': '$TEST_DIR/nbd.socket'
'path': '$SOCK_DIR/nbd.socket'
} } } }" \
'return' \
'error'

View file

@ -26,7 +26,7 @@ echo "QA output created by $seq"
status=1 # failure is the default!
MIG_SOCKET="${TEST_DIR}/migrate"
MIG_SOCKET="${SOCK_DIR}/migrate"
_cleanup()
{

View file

@ -31,7 +31,7 @@ _cleanup()
{
_cleanup_qemu
_cleanup_test_img
rm -f "$TEST_DIR/nbd"
rm -f "$SOCK_DIR/nbd"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@ -66,7 +66,7 @@ else
QEMU_COMM_TIMEOUT=1
fi
_send_qemu_cmd $h "nbd_server_start unix:$TEST_DIR/nbd" "(qemu)"
_send_qemu_cmd $h "nbd_server_start unix:$SOCK_DIR/nbd" "(qemu)"
_send_qemu_cmd $h "nbd_server_add -w drive0" "(qemu)"
_send_qemu_cmd $h "q" "(qemu)"

View file

@ -1,7 +1,7 @@
QA output created by 192
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) nbd_server_start unix:TEST_DIR/nbd
(qemu) nbd_server_start unix:SOCK_DIR/nbd
(qemu) nbd_server_add -w drive0
(qemu) q
*** done

View file

@ -26,8 +26,8 @@ iotests.verify_platform(['linux'])
with iotests.FilePath('source.img') as source_img_path, \
iotests.FilePath('dest.img') as dest_img_path, \
iotests.FilePath('migration.sock') as migration_sock_path, \
iotests.FilePath('nbd.sock') as nbd_sock_path, \
iotests.FilePaths(['migration.sock', 'nbd.sock'], iotests.sock_dir) as \
[migration_sock_path, nbd_sock_path], \
iotests.VM('source') as source_vm, \
iotests.VM('dest') as dest_vm:

View file

@ -24,7 +24,7 @@ echo "QA output created by $seq"
status=1 # failure is the default!
MIG_SOCKET="${TEST_DIR}/migrate"
MIG_SOCKET="${SOCK_DIR}/migrate"
# get standard environment, filters and checks
. ./common.rc

View file

@ -24,7 +24,7 @@ import iotests
import time
from iotests import qemu_img_create, qemu_io, filter_qemu_io, QemuIoInteractive
nbd_sock = os.path.join(iotests.test_dir, 'nbd_sock')
nbd_sock = os.path.join(iotests.sock_dir, 'nbd_sock')
nbd_uri = 'nbd+unix:///exp?socket=' + nbd_sock
disk = os.path.join(iotests.test_dir, 'disk')

View file

@ -26,7 +26,7 @@ iotests.verify_image_format(supported_fmts=['generic'])
with iotests.FilePath('disk.img') as disk_img_path, \
iotests.FilePath('disk-snapshot.img') as disk_snapshot_img_path, \
iotests.FilePath('nbd.sock') as nbd_sock_path, \
iotests.FilePath('nbd.sock', iotests.sock_dir) as nbd_sock_path, \
iotests.VM() as vm:
img_size = '10M'

View file

@ -24,7 +24,8 @@ from iotests import qemu_img_create, qemu_io, qemu_img_verbose, qemu_nbd, \
iotests.verify_image_format(supported_fmts=['qcow2'])
disk, nbd_sock = file_path('disk', 'nbd-sock')
disk = file_path('disk')
nbd_sock = file_path('nbd-sock', base_dir=iotests.sock_dir)
nbd_uri = 'nbd+unix:///exp?socket=' + nbd_sock
qemu_img_create('-f', iotests.imgfmt, disk, '1M')

View file

@ -48,7 +48,7 @@ remainder = [("0xd5", "0x108000", "32k"), # Right-end of partial-left [1]
with iotests.FilePath('base.img') as base_img_path, \
iotests.FilePath('fleece.img') as fleece_img_path, \
iotests.FilePath('nbd.sock') as nbd_sock_path, \
iotests.FilePath('nbd.sock', iotests.sock_dir) as nbd_sock_path, \
iotests.VM() as vm:
log('--- Setting up images ---')

View file

@ -28,7 +28,7 @@ _cleanup()
nbd_server_stop
_cleanup_test_img
_cleanup_qemu
rm -f "$TEST_DIR/nbd"
rm -f "$SOCK_DIR/nbd"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@ -125,11 +125,11 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
"arguments":{"device":"n"}}' "error" # Attempt add without server
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start",
"arguments":{"addr":{"type":"unix",
"data":{"path":"'"$TEST_DIR/nbd"'"}}}}' "return"
"data":{"path":"'"$SOCK_DIR/nbd"'"}}}}' "return"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start",
"arguments":{"addr":{"type":"unix",
"data":{"path":"'"$TEST_DIR/nbd"1'"}}}}' "error" # Attempt second server
$QEMU_NBD_PROG -L -k "$TEST_DIR/nbd"
"data":{"path":"'"$SOCK_DIR/nbd"1'"}}}}' "error" # Attempt second server
$QEMU_NBD_PROG -L -k "$SOCK_DIR/nbd"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
"arguments":{"device":"n", "bitmap":"b"}}' "return"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
@ -145,14 +145,14 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
"arguments":{"device":"n", "name":"n2", "writable":true,
"bitmap":"b2"}}' "return"
$QEMU_NBD_PROG -L -k "$TEST_DIR/nbd"
$QEMU_NBD_PROG -L -k "$SOCK_DIR/nbd"
echo
echo "=== Contrast normal status to large granularity dirty-bitmap ==="
echo
QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT
IMG="driver=nbd,export=n,server.type=unix,server.path=$TEST_DIR/nbd"
IMG="driver=nbd,export=n,server.type=unix,server.path=$SOCK_DIR/nbd"
$QEMU_IO -r -c 'r -P 0x22 512 512' -c 'r -P 0 512k 512k' -c 'r -P 0x11 1m 1m' \
-c 'r -P 0x33 2m 2m' --image-opts "$IMG" | _filter_qemu_io
$QEMU_IMG map --output=json --image-opts \
@ -164,7 +164,7 @@ echo
echo "=== Contrast to small granularity dirty-bitmap ==="
echo
IMG="driver=nbd,export=n2,server.type=unix,server.path=$TEST_DIR/nbd"
IMG="driver=nbd,export=n2,server.type=unix,server.path=$SOCK_DIR/nbd"
$QEMU_IMG map --output=json --image-opts \
"$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b2" | _filter_qemu_img_map

View file

@ -29,7 +29,7 @@ status=1 # failure is the default!
_cleanup()
{
rm -f "$TEST_DIR/nbd"
rm -f "$SOCK_DIR/nbd"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@ -135,7 +135,7 @@ echo
run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "read-zeroes": true, "node-name": "hd0", "read-only": true}}
{ "execute": "nbd-server-start", "arguments": {"addr":{"type":"unix","data":{"path":"$TEST_DIR/nbd"}}}}
{ "execute": "nbd-server-start", "arguments": {"addr":{"type":"unix","data":{"path":"$SOCK_DIR/nbd"}}}}
{ "execute": "nbd-server-add", "arguments": {"device":"hd0"}}
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}}

View file

@ -23,8 +23,6 @@ echo "QA output created by $seq"
status=1 # failure is the default!
nbd_unix_socket=$TEST_DIR/test_qemu_nbd_socket
_cleanup()
{
_cleanup_test_img

View file

@ -598,7 +598,7 @@ class TestBlockdevReopen(iotests.QMPTestCase):
##################
###### null ######
##################
opts = {'driver': 'null-aio', 'node-name': 'root', 'size': 1024}
opts = {'driver': 'null-co', 'node-name': 'root', 'size': 1024}
result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
self.assert_qmp(result, 'return', {})

523
tests/qemu-iotests/261 Executable file
View file

@ -0,0 +1,523 @@
#!/usr/bin/env bash
#
# Test case for qcow2's handling of extra data in snapshot table entries
# (and more generally, how certain cases of broken snapshot tables are
# handled)
#
# Copyright (C) 2019 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"
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
rm -f "$TEST_IMG".v{2,3}.orig
rm -f "$TEST_DIR"/sn{0,1,2}{,-pre,-extra,-post}
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
# This tests qocw2-specific low-level functionality
_supported_fmt qcow2
_supported_proto file
_supported_os Linux
# (1) We create a v2 image that supports nothing but refcount_bits=16
# (2) We do some refcount management on our own which expects
# refcount_bits=16
_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)'
# Parameters:
# $1: image filename
# $2: snapshot table entry offset in the image
snapshot_table_entry_size()
{
id_len=$(peek_file_be "$1" $(($2 + 12)) 2)
name_len=$(peek_file_be "$1" $(($2 + 14)) 2)
extra_len=$(peek_file_be "$1" $(($2 + 36)) 4)
full_len=$((40 + extra_len + id_len + name_len))
echo $(((full_len + 7) / 8 * 8))
}
# Parameter:
# $1: image filename
print_snapshot_table()
{
nb_entries=$(peek_file_be "$1" 60 4)
offset=$(peek_file_be "$1" 64 8)
echo "Snapshots in $1:" | _filter_testdir | _filter_imgfmt
for ((i = 0; i < nb_entries; i++)); do
id_len=$(peek_file_be "$1" $((offset + 12)) 2)
name_len=$(peek_file_be "$1" $((offset + 14)) 2)
extra_len=$(peek_file_be "$1" $((offset + 36)) 4)
extra_ofs=$((offset + 40))
id_ofs=$((extra_ofs + extra_len))
name_ofs=$((id_ofs + id_len))
echo " [$i]"
echo " ID: $(peek_file_raw "$1" $id_ofs $id_len)"
echo " Name: $(peek_file_raw "$1" $name_ofs $name_len)"
echo " Extra data size: $extra_len"
if [ $extra_len -ge 8 ]; then
echo " VM state size: $(peek_file_be "$1" $extra_ofs 8)"
fi
if [ $extra_len -ge 16 ]; then
echo " Disk size: $(peek_file_be "$1" $((extra_ofs + 8)) 8)"
fi
if [ $extra_len -gt 16 ]; then
echo ' Unknown extra data:' \
"$(peek_file_raw "$1" $((extra_ofs + 16)) $((extra_len - 16)) \
| tr -d '\0')"
fi
offset=$((offset + $(snapshot_table_entry_size "$1" $offset)))
done
}
# Mark clusters as allocated; works only in refblock 0 (i.e. before
# cluster #32768).
# Parameters:
# $1: Start offset of what to allocate
# $2: End offset (exclusive)
refblock0_allocate()
{
reftable_ofs=$(peek_file_be "$TEST_IMG" 48 8)
refblock_ofs=$(peek_file_be "$TEST_IMG" $reftable_ofs 8)
cluster=$(($1 / 65536))
ecluster=$((($2 + 65535) / 65536))
while [ $cluster -lt $ecluster ]; do
if [ $cluster -ge 32768 ]; then
echo "*** Abort: Cluster $cluster exceeds refblock 0 ***"
exit 1
fi
poke_file "$TEST_IMG" $((refblock_ofs + cluster * 2)) '\x00\x01'
cluster=$((cluster + 1))
done
}
echo
echo '=== Create v2 template ==='
echo
# Create v2 image with a snapshot table with three entries:
# [0]: No extra data (valid with v2, not valid with v3)
# [1]: Has extra data unknown to qemu
# [2]: Has the 64-bit VM state size, but not the disk size (again,
# valid with v2, not valid with v3)
TEST_IMG="$TEST_IMG.v2.orig" IMGOPTS='compat=0.10' _make_test_img 64M
$QEMU_IMG snapshot -c sn0 "$TEST_IMG.v2.orig"
$QEMU_IMG snapshot -c sn1 "$TEST_IMG.v2.orig"
$QEMU_IMG snapshot -c sn2 "$TEST_IMG.v2.orig"
# Copy out all existing snapshot table entries
sn_table_ofs=$(peek_file_be "$TEST_IMG.v2.orig" 64 8)
# ofs: Snapshot table entry offset
# eds: Extra data size
# ids: Name + ID size
# len: Total entry length
sn0_ofs=$sn_table_ofs
sn0_eds=$(peek_file_be "$TEST_IMG.v2.orig" $((sn0_ofs + 36)) 4)
sn0_ids=$(($(peek_file_be "$TEST_IMG.v2.orig" $((sn0_ofs + 12)) 2) +
$(peek_file_be "$TEST_IMG.v2.orig" $((sn0_ofs + 14)) 2)))
sn0_len=$(snapshot_table_entry_size "$TEST_IMG.v2.orig" $sn0_ofs)
sn1_ofs=$((sn0_ofs + sn0_len))
sn1_eds=$(peek_file_be "$TEST_IMG.v2.orig" $((sn1_ofs + 36)) 4)
sn1_ids=$(($(peek_file_be "$TEST_IMG.v2.orig" $((sn1_ofs + 12)) 2) +
$(peek_file_be "$TEST_IMG.v2.orig" $((sn1_ofs + 14)) 2)))
sn1_len=$(snapshot_table_entry_size "$TEST_IMG.v2.orig" $sn1_ofs)
sn2_ofs=$((sn1_ofs + sn1_len))
sn2_eds=$(peek_file_be "$TEST_IMG.v2.orig" $((sn2_ofs + 36)) 4)
sn2_ids=$(($(peek_file_be "$TEST_IMG.v2.orig" $((sn2_ofs + 12)) 2) +
$(peek_file_be "$TEST_IMG.v2.orig" $((sn2_ofs + 14)) 2)))
sn2_len=$(snapshot_table_entry_size "$TEST_IMG.v2.orig" $sn2_ofs)
# Data before extra data
dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn0-pre" bs=1 skip=$sn0_ofs count=40 \
&> /dev/null
dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn1-pre" bs=1 skip=$sn1_ofs count=40 \
&> /dev/null
dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn2-pre" bs=1 skip=$sn2_ofs count=40 \
&> /dev/null
# Extra data
dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn0-extra" bs=1 \
skip=$((sn0_ofs + 40)) count=$sn0_eds &> /dev/null
dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn1-extra" bs=1 \
skip=$((sn1_ofs + 40)) count=$sn1_eds &> /dev/null
dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn2-extra" bs=1 \
skip=$((sn2_ofs + 40)) count=$sn2_eds &> /dev/null
# Data after extra data
dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn0-post" bs=1 \
skip=$((sn0_ofs + 40 + sn0_eds)) count=$sn0_ids \
&> /dev/null
dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn1-post" bs=1 \
skip=$((sn1_ofs + 40 + sn1_eds)) count=$sn1_ids \
&> /dev/null
dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn2-post" bs=1 \
skip=$((sn2_ofs + 40 + sn2_eds)) count=$sn2_ids \
&> /dev/null
# Amend them, one by one
# Set sn0's extra data size to 0
poke_file "$TEST_DIR/sn0-pre" 36 '\x00\x00\x00\x00'
truncate -s 0 "$TEST_DIR/sn0-extra"
# Grow sn0-post to pad
truncate -s $(($(snapshot_table_entry_size "$TEST_DIR/sn0-pre") - 40)) \
"$TEST_DIR/sn0-post"
# Set sn1's extra data size to 42
poke_file "$TEST_DIR/sn1-pre" 36 '\x00\x00\x00\x2a'
truncate -s 42 "$TEST_DIR/sn1-extra"
poke_file "$TEST_DIR/sn1-extra" 16 'very important data'
# Grow sn1-post to pad
truncate -s $(($(snapshot_table_entry_size "$TEST_DIR/sn1-pre") - 82)) \
"$TEST_DIR/sn1-post"
# Set sn2's extra data size to 8
poke_file "$TEST_DIR/sn2-pre" 36 '\x00\x00\x00\x08'
truncate -s 8 "$TEST_DIR/sn2-extra"
# Grow sn2-post to pad
truncate -s $(($(snapshot_table_entry_size "$TEST_DIR/sn2-pre") - 48)) \
"$TEST_DIR/sn2-post"
# Construct snapshot table
cat "$TEST_DIR"/sn0-{pre,extra,post} \
"$TEST_DIR"/sn1-{pre,extra,post} \
"$TEST_DIR"/sn2-{pre,extra,post} \
| dd of="$TEST_IMG.v2.orig" bs=1 seek=$sn_table_ofs conv=notrunc \
&> /dev/null
# Done!
TEST_IMG="$TEST_IMG.v2.orig" _check_test_img
print_snapshot_table "$TEST_IMG.v2.orig"
echo
echo '=== Upgrade to v3 ==='
echo
cp "$TEST_IMG.v2.orig" "$TEST_IMG.v3.orig"
$QEMU_IMG amend -o compat=1.1 "$TEST_IMG.v3.orig"
TEST_IMG="$TEST_IMG.v3.orig" _check_test_img
print_snapshot_table "$TEST_IMG.v3.orig"
echo
echo '=== Repair botched v3 ==='
echo
# Force the v2 file to be v3. v3 requires each snapshot table entry
# to have at least 16 bytes of extra data, so it will not comply to
# the qcow2 v3 specification; but we can fix that.
cp "$TEST_IMG.v2.orig" "$TEST_IMG"
# Set version
poke_file "$TEST_IMG" 4 '\x00\x00\x00\x03'
# Increase header length (necessary for v3)
poke_file "$TEST_IMG" 100 '\x00\x00\x00\x68'
# Set refcount order (necessary for v3)
poke_file "$TEST_IMG" 96 '\x00\x00\x00\x04'
_check_test_img -r all
print_snapshot_table "$TEST_IMG"
# From now on, just test the qcow2 version we are supposed to test.
# (v3 by default, v2 by choice through $IMGOPTS.)
# That works because we always write all known extra data when
# updating the snapshot table, independent of the version.
if echo "$IMGOPTS" | grep -q 'compat=\(0\.10\|v2\)' 2> /dev/null; then
subver=v2
else
subver=v3
fi
echo
echo '=== Add new snapshot ==='
echo
cp "$TEST_IMG.$subver.orig" "$TEST_IMG"
$QEMU_IMG snapshot -c sn3 "$TEST_IMG"
_check_test_img
print_snapshot_table "$TEST_IMG"
echo
echo '=== Remove different snapshots ==='
for sn in sn0 sn1 sn2; do
echo
echo "--- $sn ---"
cp "$TEST_IMG.$subver.orig" "$TEST_IMG"
$QEMU_IMG snapshot -d $sn "$TEST_IMG"
_check_test_img
print_snapshot_table "$TEST_IMG"
done
echo
echo '=== Reject too much unknown extra data ==='
echo
cp "$TEST_IMG.$subver.orig" "$TEST_IMG"
$QEMU_IMG snapshot -c sn3 "$TEST_IMG"
sn_table_ofs=$(peek_file_be "$TEST_IMG" 64 8)
sn0_ofs=$sn_table_ofs
sn1_ofs=$((sn0_ofs + $(snapshot_table_entry_size "$TEST_IMG" $sn0_ofs)))
sn2_ofs=$((sn1_ofs + $(snapshot_table_entry_size "$TEST_IMG" $sn1_ofs)))
sn3_ofs=$((sn2_ofs + $(snapshot_table_entry_size "$TEST_IMG" $sn2_ofs)))
# 64 kB of extra data should be rejected
# (Note that this also induces a refcount error, because it spills
# over to the next cluster. That's a good way to test that we can
# handle simultaneous snapshot table and refcount errors.)
poke_file "$TEST_IMG" $((sn3_ofs + 36)) '\x00\x01\x00\x00'
# Print error
_img_info
echo
_check_test_img
echo
# Should be repairable
_check_test_img -r all
echo
echo '=== Snapshot table too big ==='
echo
sn_table_ofs=$(peek_file_be "$TEST_IMG.v3.orig" 64 8)
# Fill a snapshot with 1 kB of extra data, a 65535-char ID, and a
# 65535-char name, and repeat it as many times as necessary to fill
# 64 MB (the maximum supported by qemu)
touch "$TEST_DIR/sn0"
# Full size (fixed + extra + ID + name + padding)
sn_size=$((40 + 1024 + 65535 + 65535 + 2))
# We only need the fixed part, though.
truncate -s 40 "$TEST_DIR/sn0"
# 65535-char ID string
poke_file "$TEST_DIR/sn0" 12 '\xff\xff'
# 65535-char name
poke_file "$TEST_DIR/sn0" 14 '\xff\xff'
# 1 kB of extra data
poke_file "$TEST_DIR/sn0" 36 '\x00\x00\x04\x00'
# Create test image
_make_test_img 64M
# Hook up snapshot table somewhere safe (at 1 MB)
poke_file "$TEST_IMG" 64 '\x00\x00\x00\x00\x00\x10\x00\x00'
offset=1048576
size_written=0
sn_count=0
while [ $size_written -le $((64 * 1048576)) ]; do
dd if="$TEST_DIR/sn0" of="$TEST_IMG" bs=1 seek=$offset conv=notrunc \
&> /dev/null
offset=$((offset + sn_size))
size_written=$((size_written + sn_size))
sn_count=$((sn_count + 1))
done
truncate -s "$offset" "$TEST_IMG"
# Give the last snapshot (the one to be removed) an L1 table so we can
# see how that is handled when repairing the image
# (Put it two clusters before 1 MB, and one L2 table one cluster
# before 1 MB)
poke_file "$TEST_IMG" $((offset - sn_size + 0)) \
'\x00\x00\x00\x00\x00\x0e\x00\x00'
poke_file "$TEST_IMG" $((offset - sn_size + 8)) \
'\x00\x00\x00\x01'
# Hook up the L2 table
poke_file "$TEST_IMG" $((1048576 - 2 * 65536)) \
'\x80\x00\x00\x00\x00\x0f\x00\x00'
# Make sure all of the clusters we just hooked up are allocated:
# - The snapshot table
# - The last snapshot's L1 and L2 table
refblock0_allocate $((1048576 - 2 * 65536)) $offset
poke_file "$TEST_IMG" 60 \
"$(printf '%08x' $sn_count | sed -e 's/\(..\)/\\x\1/g')"
# Print error
_img_info
echo
_check_test_img
echo
# Should be repairable
_check_test_img -r all
echo
echo "$((sn_count - 1)) snapshots should remain:"
echo " qemu-img info reports $(_img_info | grep -c '^ \{34\}') snapshots"
echo " Image header reports $(peek_file_be "$TEST_IMG" 60 4) snapshots"
echo
echo '=== Snapshot table too big with one entry with too much extra data ==='
echo
# For this test, we reuse the image from the previous case, which has
# a snapshot table that is right at the limit.
# Our layout looks like this:
# - (a number of snapshot table entries)
# - One snapshot with $extra_data_size extra data
# - One normal snapshot that breaks the 64 MB boundary
# - One normal snapshot beyond the 64 MB boundary
#
# $extra_data_size is calculated so that simply by virtue of it
# decreasing to 1 kB, the penultimate snapshot will fit into 64 MB
# limit again. The final snapshot will always be beyond the limit, so
# that we can see that the repair algorithm does still determine the
# limit to be somewhere, even when truncating one snapshot's extra
# data.
# The last case has removed the last snapshot, so calculate
# $old_offset to get the current image's real length
old_offset=$((offset - sn_size))
# The layout from the previous test had one snapshot beyond the 64 MB
# limit; we want the same (after the oversized extra data has been
# truncated to 1 kB), so we drop the last three snapshots and
# construct them from scratch.
offset=$((offset - 3 * sn_size))
sn_count=$((sn_count - 3))
# Assuming we had already written one of the three snapshots
# (necessary so we can calculate $extra_data_size next).
size_written=$((size_written - 2 * sn_size))
# Increase the extra data size so we go past the limit
# (The -1024 comes from the 1 kB of extra data we already have)
extra_data_size=$((64 * 1048576 + 8 - sn_size - (size_written - 1024)))
poke_file "$TEST_IMG" $((offset + 36)) \
"$(printf '%08x' $extra_data_size | sed -e 's/\(..\)/\\x\1/g')"
offset=$((offset + sn_size - 1024 + extra_data_size))
size_written=$((size_written - 1024 + extra_data_size))
sn_count=$((sn_count + 1))
# Write the two normal snapshots
for ((i = 0; i < 2; i++)); do
dd if="$TEST_DIR/sn0" of="$TEST_IMG" bs=1 seek=$offset conv=notrunc \
&> /dev/null
offset=$((offset + sn_size))
size_written=$((size_written + sn_size))
sn_count=$((sn_count + 1))
if [ $i = 0 ]; then
# Check that the penultimate snapshot is beyond the 64 MB limit
echo "Snapshot table size should equal $((64 * 1048576 + 8)):" \
$size_written
echo
fi
done
truncate -s $offset "$TEST_IMG"
refblock0_allocate $old_offset $offset
poke_file "$TEST_IMG" 60 \
"$(printf '%08x' $sn_count | sed -e 's/\(..\)/\\x\1/g')"
# Print error
_img_info
echo
_check_test_img
echo
# Just truncating the extra data should be sufficient to shorten the
# snapshot table so only one snapshot exceeds the extra size
_check_test_img -r all
echo
echo '=== Too many snapshots ==='
echo
# Create a v2 image, for speeds' sake: All-zero snapshot table entries
# are only valid in v2.
IMGOPTS='compat=0.10' _make_test_img 64M
# Hook up snapshot table somewhere safe (at 1 MB)
poke_file "$TEST_IMG" 64 '\x00\x00\x00\x00\x00\x10\x00\x00'
# "Create" more than 65536 snapshots (twice that many here)
poke_file "$TEST_IMG" 60 '\x00\x02\x00\x00'
# 40-byte all-zero snapshot table entries are valid snapshots, but
# only in v2 (v3 needs 16 bytes of extra data, so we would have to
# write 131072x '\x10').
truncate -s $((1048576 + 40 * 131072)) "$TEST_IMG"
# But let us give one of the snapshots to be removed an L1 table so
# we can see how that is handled when repairing the image.
# (Put it two clusters before 1 MB, and one L2 table one cluster
# before 1 MB)
poke_file "$TEST_IMG" $((1048576 + 40 * 65536 + 0)) \
'\x00\x00\x00\x00\x00\x0e\x00\x00'
poke_file "$TEST_IMG" $((1048576 + 40 * 65536 + 8)) \
'\x00\x00\x00\x01'
# Hook up the L2 table
poke_file "$TEST_IMG" $((1048576 - 2 * 65536)) \
'\x80\x00\x00\x00\x00\x0f\x00\x00'
# Make sure all of the clusters we just hooked up are allocated:
# - The snapshot table
# - The last snapshot's L1 and L2 table
refblock0_allocate $((1048576 - 2 * 65536)) $((1048576 + 40 * 131072))
# Print error
_img_info
echo
_check_test_img
echo
# Should be repairable
_check_test_img -r all
echo
echo '65536 snapshots should remain:'
echo " qemu-img info reports $(_img_info | grep -c '^ \{34\}') snapshots"
echo " Image header reports $(peek_file_be "$TEST_IMG" 60 4) snapshots"
# success, all done
echo "*** done"
status=0

346
tests/qemu-iotests/261.out Normal file
View file

@ -0,0 +1,346 @@
QA output created by 261
=== Create v2 template ===
Formatting 'TEST_DIR/t.IMGFMT.v2.orig', fmt=IMGFMT size=67108864
No errors were found on the image.
Snapshots in TEST_DIR/t.IMGFMT.v2.orig:
[0]
ID: 1
Name: sn0
Extra data size: 0
[1]
ID: 2
Name: sn1
Extra data size: 42
VM state size: 0
Disk size: 67108864
Unknown extra data: very important data
[2]
ID: 3
Name: sn2
Extra data size: 8
VM state size: 0
=== Upgrade to v3 ===
No errors were found on the image.
Snapshots in TEST_DIR/t.IMGFMT.v3.orig:
[0]
ID: 1
Name: sn0
Extra data size: 16
VM state size: 0
Disk size: 67108864
[1]
ID: 2
Name: sn1
Extra data size: 42
VM state size: 0
Disk size: 67108864
Unknown extra data: very important data
[2]
ID: 3
Name: sn2
Extra data size: 16
VM state size: 0
Disk size: 67108864
=== Repair botched v3 ===
Repairing snapshot table entry 0 is incomplete
Repairing snapshot table entry 2 is incomplete
The following inconsistencies were found and repaired:
0 leaked clusters
2 corruptions
Double checking the fixed image now...
No errors were found on the image.
Snapshots in TEST_DIR/t.IMGFMT:
[0]
ID: 1
Name: sn0
Extra data size: 16
VM state size: 0
Disk size: 67108864
[1]
ID: 2
Name: sn1
Extra data size: 42
VM state size: 0
Disk size: 67108864
Unknown extra data: very important data
[2]
ID: 3
Name: sn2
Extra data size: 16
VM state size: 0
Disk size: 67108864
=== Add new snapshot ===
No errors were found on the image.
Snapshots in TEST_DIR/t.IMGFMT:
[0]
ID: 1
Name: sn0
Extra data size: 16
VM state size: 0
Disk size: 67108864
[1]
ID: 2
Name: sn1
Extra data size: 42
VM state size: 0
Disk size: 67108864
Unknown extra data: very important data
[2]
ID: 3
Name: sn2
Extra data size: 16
VM state size: 0
Disk size: 67108864
[3]
ID: 4
Name: sn3
Extra data size: 16
VM state size: 0
Disk size: 67108864
=== Remove different snapshots ===
--- sn0 ---
No errors were found on the image.
Snapshots in TEST_DIR/t.IMGFMT:
[0]
ID: 2
Name: sn1
Extra data size: 42
VM state size: 0
Disk size: 67108864
Unknown extra data: very important data
[1]
ID: 3
Name: sn2
Extra data size: 16
VM state size: 0
Disk size: 67108864
--- sn1 ---
No errors were found on the image.
Snapshots in TEST_DIR/t.IMGFMT:
[0]
ID: 1
Name: sn0
Extra data size: 16
VM state size: 0
Disk size: 67108864
[1]
ID: 3
Name: sn2
Extra data size: 16
VM state size: 0
Disk size: 67108864
--- sn2 ---
No errors were found on the image.
Snapshots in TEST_DIR/t.IMGFMT:
[0]
ID: 1
Name: sn0
Extra data size: 16
VM state size: 0
Disk size: 67108864
[1]
ID: 2
Name: sn1
Extra data size: 42
VM state size: 0
Disk size: 67108864
Unknown extra data: very important data
=== Reject too much unknown extra data ===
qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Too much extra metadata in snapshot table entry 3
You can force-remove this extra metadata with qemu-img check -r all
qemu-img: ERROR failed to read the snapshot table: Too much extra metadata in snapshot table entry 3
You can force-remove this extra metadata with qemu-img check -r all
qemu-img: Check failed: File too large
Discarding too much extra metadata in snapshot table entry 3 (65536 > 1024)
ERROR cluster 10 refcount=0 reference=1
Rebuilding refcount structure
Repairing cluster 1 refcount=1 reference=0
Repairing cluster 2 refcount=1 reference=0
The following inconsistencies were found and repaired:
0 leaked clusters
2 corruptions
Double checking the fixed image now...
No errors were found on the image.
=== Snapshot table too big ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Snapshot table is too big
You can force-remove all 1 overhanging snapshots with qemu-img check -r all
qemu-img: ERROR failed to read the snapshot table: Snapshot table is too big
You can force-remove all 1 overhanging snapshots with qemu-img check -r all
qemu-img: Check failed: File too large
Discarding 1 overhanging snapshots (snapshot table is too big)
Leaked cluster 14 refcount=1 reference=0
Leaked cluster 15 refcount=1 reference=0
Leaked cluster 1039 refcount=1 reference=0
Leaked cluster 1040 refcount=1 reference=0
Repairing cluster 14 refcount=1 reference=0
Repairing cluster 15 refcount=1 reference=0
Repairing cluster 1039 refcount=1 reference=0
Repairing cluster 1040 refcount=1 reference=0
The following inconsistencies were found and repaired:
4 leaked clusters
1 corruptions
Double checking the fixed image now...
No errors were found on the image.
507 snapshots should remain:
qemu-img info reports 507 snapshots
Image header reports 507 snapshots
=== Snapshot table too big with one entry with too much extra data ===
Snapshot table size should equal 67108872: 67108872
qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Too much extra metadata in snapshot table entry 505
You can force-remove this extra metadata with qemu-img check -r all
qemu-img: ERROR failed to read the snapshot table: Too much extra metadata in snapshot table entry 505
You can force-remove this extra metadata with qemu-img check -r all
qemu-img: Check failed: File too large
Discarding too much extra metadata in snapshot table entry 505 (116944 > 1024)
Discarding 1 overhanging snapshots (snapshot table is too big)
Leaked cluster 1041 refcount=1 reference=0
Leaked cluster 1042 refcount=1 reference=0
Repairing cluster 1041 refcount=1 reference=0
Repairing cluster 1042 refcount=1 reference=0
The following inconsistencies were found and repaired:
2 leaked clusters
2 corruptions
Double checking the fixed image now...
No errors were found on the image.
=== Too many snapshots ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Snapshot table too large
qemu-img: ERROR snapshot table too large
You can force-remove all 65536 overhanging snapshots with qemu-img check -r all
qemu-img: Check failed: File too large
Discarding 65536 overhanging snapshots
Leaked cluster 14 refcount=1 reference=0
Leaked cluster 15 refcount=1 reference=0
Leaked cluster 56 refcount=1 reference=0
Leaked cluster 57 refcount=1 reference=0
Leaked cluster 58 refcount=1 reference=0
Leaked cluster 59 refcount=1 reference=0
Leaked cluster 60 refcount=1 reference=0
Leaked cluster 61 refcount=1 reference=0
Leaked cluster 62 refcount=1 reference=0
Leaked cluster 63 refcount=1 reference=0
Leaked cluster 64 refcount=1 reference=0
Leaked cluster 65 refcount=1 reference=0
Leaked cluster 66 refcount=1 reference=0
Leaked cluster 67 refcount=1 reference=0
Leaked cluster 68 refcount=1 reference=0
Leaked cluster 69 refcount=1 reference=0
Leaked cluster 70 refcount=1 reference=0
Leaked cluster 71 refcount=1 reference=0
Leaked cluster 72 refcount=1 reference=0
Leaked cluster 73 refcount=1 reference=0
Leaked cluster 74 refcount=1 reference=0
Leaked cluster 75 refcount=1 reference=0
Leaked cluster 76 refcount=1 reference=0
Leaked cluster 77 refcount=1 reference=0
Leaked cluster 78 refcount=1 reference=0
Leaked cluster 79 refcount=1 reference=0
Leaked cluster 80 refcount=1 reference=0
Leaked cluster 81 refcount=1 reference=0
Leaked cluster 82 refcount=1 reference=0
Leaked cluster 83 refcount=1 reference=0
Leaked cluster 84 refcount=1 reference=0
Leaked cluster 85 refcount=1 reference=0
Leaked cluster 86 refcount=1 reference=0
Leaked cluster 87 refcount=1 reference=0
Leaked cluster 88 refcount=1 reference=0
Leaked cluster 89 refcount=1 reference=0
Leaked cluster 90 refcount=1 reference=0
Leaked cluster 91 refcount=1 reference=0
Leaked cluster 92 refcount=1 reference=0
Leaked cluster 93 refcount=1 reference=0
Leaked cluster 94 refcount=1 reference=0
Leaked cluster 95 refcount=1 reference=0
Repairing cluster 14 refcount=1 reference=0
Repairing cluster 15 refcount=1 reference=0
Repairing cluster 56 refcount=1 reference=0
Repairing cluster 57 refcount=1 reference=0
Repairing cluster 58 refcount=1 reference=0
Repairing cluster 59 refcount=1 reference=0
Repairing cluster 60 refcount=1 reference=0
Repairing cluster 61 refcount=1 reference=0
Repairing cluster 62 refcount=1 reference=0
Repairing cluster 63 refcount=1 reference=0
Repairing cluster 64 refcount=1 reference=0
Repairing cluster 65 refcount=1 reference=0
Repairing cluster 66 refcount=1 reference=0
Repairing cluster 67 refcount=1 reference=0
Repairing cluster 68 refcount=1 reference=0
Repairing cluster 69 refcount=1 reference=0
Repairing cluster 70 refcount=1 reference=0
Repairing cluster 71 refcount=1 reference=0
Repairing cluster 72 refcount=1 reference=0
Repairing cluster 73 refcount=1 reference=0
Repairing cluster 74 refcount=1 reference=0
Repairing cluster 75 refcount=1 reference=0
Repairing cluster 76 refcount=1 reference=0
Repairing cluster 77 refcount=1 reference=0
Repairing cluster 78 refcount=1 reference=0
Repairing cluster 79 refcount=1 reference=0
Repairing cluster 80 refcount=1 reference=0
Repairing cluster 81 refcount=1 reference=0
Repairing cluster 82 refcount=1 reference=0
Repairing cluster 83 refcount=1 reference=0
Repairing cluster 84 refcount=1 reference=0
Repairing cluster 85 refcount=1 reference=0
Repairing cluster 86 refcount=1 reference=0
Repairing cluster 87 refcount=1 reference=0
Repairing cluster 88 refcount=1 reference=0
Repairing cluster 89 refcount=1 reference=0
Repairing cluster 90 refcount=1 reference=0
Repairing cluster 91 refcount=1 reference=0
Repairing cluster 92 refcount=1 reference=0
Repairing cluster 93 refcount=1 reference=0
Repairing cluster 94 refcount=1 reference=0
Repairing cluster 95 refcount=1 reference=0
The following inconsistencies were found and repaired:
42 leaked clusters
65536 corruptions
Double checking the fixed image now...
No errors were found on the image.
65536 snapshots should remain:
qemu-img info reports 65536 snapshots
Image header reports 65536 snapshots
*** done

View file

@ -24,6 +24,8 @@ import iotests
from iotests import qemu_img_create, qemu_io_silent_check, file_path, \
qemu_nbd_popen, log
iotests.verify_image_format(supported_fmts=['qcow2'])
disk_a, disk_b, nbd_sock = file_path('disk_a', 'disk_b', 'nbd-sock')
nbd_uri = 'nbd+unix:///?socket=' + nbd_sock
size = 5 * 1024 * 1024

View file

@ -29,7 +29,7 @@ status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
rm -f "$TEST_DIR/nbd"
rm -f "$SOCK_DIR/nbd"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@ -143,7 +143,7 @@ echo
IMGOPTS="backing_file=$TEST_IMG.base" _make_test_img $size
cat <<EOF |
nbd_server_start unix:$TEST_DIR/nbd
nbd_server_start unix:$SOCK_DIR/nbd
nbd_server_add -w backing-fmt
savevm snap0
info snapshots

View file

@ -161,7 +161,7 @@ Internal snapshots on backing file:
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT.base,node-name=backing-file -blockdev driver=IMGFMT,file=backing-file,node-name=backing-fmt -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=file -blockdev driver=IMGFMT,file=file,backing=backing-fmt,node-name=fmt
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) nbd_server_start unix:TEST_DIR/nbd
(qemu) nbd_server_start unix:SOCK_DIR/nbd
(qemu) nbd_server_add -w backing-fmt
(qemu) savevm snap0
(qemu) info snapshots

View file

@ -97,6 +97,7 @@ IMGFMT -- $FULL_IMGFMT_DETAILS
IMGPROTO -- $IMGPROTO
PLATFORM -- $FULL_HOST_DETAILS
TEST_DIR -- $TEST_DIR
SOCK_DIR -- $SOCK_DIR
SOCKET_SCM_HELPER -- $SOCKET_SCM_HELPER
EOF
@ -116,10 +117,14 @@ set_prog_path()
if [ -z "$TEST_DIR" ]; then
TEST_DIR=$PWD/scratch
fi
mkdir -p "$TEST_DIR" || _init_error 'Failed to create TEST_DIR'
if [ ! -e "$TEST_DIR" ]; then
mkdir "$TEST_DIR"
tmp_sock_dir=false
if [ -z "$SOCK_DIR" ]; then
SOCK_DIR=$(mktemp -d)
tmp_sock_dir=true
fi
mkdir -p "$SOCK_DIR" || _init_error 'Failed to create SOCK_DIR'
diff="diff -u"
verbose=false
@ -534,6 +539,7 @@ if [ -z "$SAMPLE_IMG_DIR" ]; then
fi
export TEST_DIR
export SOCK_DIR
export SAMPLE_IMG_DIR
if [ -s $tmp.list ]
@ -716,6 +722,11 @@ END { if (NR > 0) {
rm -f "${TEST_DIR}"/*.out "${TEST_DIR}"/*.err "${TEST_DIR}"/*.time
rm -f "${TEST_DIR}"/check.pid "${TEST_DIR}"/check.sts
rm -f $tmp.*
if $tmp_sock_dir
then
rm -rf "$SOCK_DIR"
fi
}
trap "_wrapup; exit \$status" 0 1 2 3 15

View file

@ -43,7 +43,8 @@ _filter_qom_path()
# replace occurrences of the actual TEST_DIR value with TEST_DIR
_filter_testdir()
{
$SED -e "s#$TEST_DIR/#TEST_DIR/#g"
$SED -e "s#$TEST_DIR/#TEST_DIR/#g" \
-e "s#$SOCK_DIR/#SOCK_DIR/#g"
}
# replace occurrences of the actual IMGFMT value with IMGFMT
@ -124,6 +125,7 @@ _filter_img_create()
$SED -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
-e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
-e "s#$TEST_DIR#TEST_DIR#g" \
-e "s#$SOCK_DIR#SOCK_DIR#g" \
-e "s#$IMGFMT#IMGFMT#g" \
-e 's#nbd:127.0.0.1:10810#TEST_DIR/t.IMGFMT#g' \
-e "s# encryption=off##g" \
@ -160,6 +162,7 @@ _filter_img_info()
$SED -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
-e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
-e "s#$TEST_DIR#TEST_DIR#g" \
-e "s#$SOCK_DIR#SOCK_DIR#g" \
-e "s#$IMGFMT#IMGFMT#g" \
-e 's#nbd://127.0.0.1:10810$#TEST_DIR/t.IMGFMT#g' \
-e 's#json.*vdisk-id.*vxhs"}}#TEST_DIR/t.IMGFMT#' \
@ -218,7 +221,7 @@ _filter_nbd()
# Filter out the TCP port number since this changes between runs.
$SED -e '/nbd\/.*\.c:/d' \
-e 's#127\.0\.0\.1:[0-9]*#127.0.0.1:PORT#g' \
-e "s#?socket=$TEST_DIR#?socket=TEST_DIR#g" \
-e "s#?socket=$SOCK_DIR#?socket=SOCK_DIR#g" \
-e 's#\(foo\|PORT/\?\|.sock\): Failed to .*$#\1#'
}

View file

@ -19,7 +19,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
nbd_unix_socket="${TEST_DIR}/qemu-nbd.sock"
nbd_unix_socket="${SOCK_DIR}/qemu-nbd.sock"
nbd_tcp_addr="127.0.0.1"
nbd_pid_file="${TEST_DIR}/qemu-nbd.pid"
nbd_stderr_fifo="${TEST_DIR}/qemu-nbd.fifo"

View file

@ -53,6 +53,26 @@ poke_file()
printf "$3" | dd "of=$1" bs=1 "seek=$2" conv=notrunc &>/dev/null
}
# peek_file_le 'test.img' 512 2 => 65534
peek_file_le()
{
# Wrap in echo $() to strip spaces
echo $(od -j"$2" -N"$3" --endian=little -An -vtu"$3" "$1")
}
# peek_file_be 'test.img' 512 2 => 65279
peek_file_be()
{
# Wrap in echo $() to strip spaces
echo $(od -j"$2" -N"$3" --endian=big -An -vtu"$3" "$1")
}
# peek_file_raw 'test.img' 512 2 => '\xff\xfe'
peek_file_raw()
{
dd if="$1" bs=1 skip="$2" count="$3" status=none
}
if ! . ./common.config
then

View file

@ -274,6 +274,7 @@
257 rw
258 rw quick
260 rw quick
261 rw
262 rw quick migration
263 rw quick
264 rw

View file

@ -57,6 +57,7 @@
imgfmt = os.environ.get('IMGFMT', 'raw')
imgproto = os.environ.get('IMGPROTO', 'file')
test_dir = os.environ.get('TEST_DIR')
sock_dir = os.environ.get('SOCK_DIR')
output_dir = os.environ.get('OUTPUT_DIR', '.')
cachemode = os.environ.get('CACHEMODE')
qemu_default_machine = os.environ.get('QEMU_DEFAULT_MACHINE')
@ -385,10 +386,10 @@ class FilePaths(object):
qemu_img('create', img_path, '1G')
# migration_sock_path is automatically deleted
"""
def __init__(self, names):
def __init__(self, names, base_dir=test_dir):
self.paths = []
for name in names:
self.paths.append(os.path.join(test_dir, file_pattern(name)))
self.paths.append(os.path.join(base_dir, file_pattern(name)))
def __enter__(self):
return self.paths
@ -405,8 +406,8 @@ class FilePath(FilePaths):
"""
FilePath is a specialization of FilePaths that takes a single filename.
"""
def __init__(self, name):
super(FilePath, self).__init__([name])
def __init__(self, name, base_dir=test_dir):
super(FilePath, self).__init__([name], base_dir)
def __enter__(self):
return self.paths[0]
@ -419,7 +420,7 @@ def file_path_remover():
pass
def file_path(*names):
def file_path(*names, base_dir=test_dir):
''' Another way to get auto-generated filename that cleans itself up.
Use is as simple as:
@ -435,7 +436,7 @@ def file_path(*names):
paths = []
for name in names:
filename = file_pattern(name)
path = os.path.join(test_dir, filename)
path = os.path.join(base_dir, filename)
file_path_remover.paths.append(path)
paths.append(path)
@ -456,7 +457,8 @@ def __init__(self, path_suffix=''):
name = "qemu%s-%d" % (path_suffix, os.getpid())
super(VM, self).__init__(qemu_prog, qemu_opts, name=name,
test_dir=test_dir,
socket_scm_helper=socket_scm_helper)
socket_scm_helper=socket_scm_helper,
sock_dir=sock_dir)
self._num_drives = 0
def add_object(self, opts):
@ -838,6 +840,11 @@ def pause_job(self, job_id='job0', wait=True):
return self.pause_wait(job_id)
return result
def case_skip(self, reason):
'''Skip this test case'''
case_notrun(reason)
self.skipTest(reason)
def notrun(reason):
'''Skip this test suite'''
@ -849,7 +856,11 @@ def notrun(reason):
sys.exit(0)
def case_notrun(reason):
'''Skip this test case'''
'''Mark this test case as not having been run (without actually
skipping it, that is left to the caller). See
QMPTestCase.case_skip() for a variant that actually skips the
current test case.'''
# Each test in qemu-iotests has a number ("seq")
seq = os.path.basename(sys.argv[0])
@ -912,22 +923,34 @@ def qemu_pipe(*args):
def supported_formats(read_only=False):
'''Set 'read_only' to True to check ro-whitelist
Otherwise, rw-whitelist is checked'''
format_message = qemu_pipe("-drive", "format=help")
line = 1 if read_only else 0
return format_message.splitlines()[line].split(":")[1].split()
if not hasattr(supported_formats, "formats"):
supported_formats.formats = {}
if read_only not in supported_formats.formats:
format_message = qemu_pipe("-drive", "format=help")
line = 1 if read_only else 0
supported_formats.formats[read_only] = \
format_message.splitlines()[line].split(":")[1].split()
return supported_formats.formats[read_only]
def skip_if_unsupported(required_formats=[], read_only=False):
'''Skip Test Decorator
Runs the test if all the required formats are whitelisted'''
def skip_test_decorator(func):
def func_wrapper(*args, **kwargs):
usf_list = list(set(required_formats) -
set(supported_formats(read_only)))
if usf_list:
case_notrun('{}: formats {} are not whitelisted'.format(
args[0], usf_list))
def func_wrapper(test_case: QMPTestCase, *args, **kwargs):
if callable(required_formats):
fmts = required_formats(test_case)
else:
return func(*args, **kwargs)
fmts = required_formats
usf_list = list(set(fmts) - set(supported_formats(read_only)))
if usf_list:
test_case.case_skip('{}: formats {} are not whitelisted'.format(
test_case, usf_list))
else:
return func(test_case, *args, **kwargs)
return func_wrapper
return skip_test_decorator
@ -950,8 +973,15 @@ def execute_unittest(output, verbosity, debug):
unittest.main(testRunner=runner)
finally:
if not debug:
sys.stderr.write(re.sub(r'Ran (\d+) tests? in [\d.]+s',
r'Ran \1 tests', output.getvalue()))
out = output.getvalue()
out = re.sub(r'Ran (\d+) tests? in [\d.]+s', r'Ran \1 tests', out)
# Hide skipped tests from the reference output
out = re.sub(r'OK \(skipped=\d+\)', 'OK', out)
out_first_line, out_rest = out.split('\n', 1)
out = out_first_line.replace('s', '.') + '\n' + out_rest
sys.stderr.write(out)
def execute_test(test_function=None,
supported_fmts=[], supported_oses=['linux'],

View file

@ -45,7 +45,7 @@ static int coroutine_fn bdrv_test_co_pdiscard(BlockDriverState *bs,
}
static int coroutine_fn
bdrv_test_co_truncate(BlockDriverState *bs, int64_t offset,
bdrv_test_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
PreallocMode prealloc, Error **errp)
{
return 0;
@ -185,18 +185,18 @@ static void test_sync_op_truncate(BdrvChild *c)
int ret;
/* Normal success path */
ret = bdrv_truncate(c, 65536, PREALLOC_MODE_OFF, NULL);
ret = bdrv_truncate(c, 65536, false, PREALLOC_MODE_OFF, NULL);
g_assert_cmpint(ret, ==, 0);
/* Early error: Negative offset */
ret = bdrv_truncate(c, -2, PREALLOC_MODE_OFF, NULL);
ret = bdrv_truncate(c, -2, false, PREALLOC_MODE_OFF, NULL);
g_assert_cmpint(ret, ==, -EINVAL);
/* Error: Read-only image */
c->bs->read_only = true;
c->bs->open_flags &= ~BDRV_O_RDWR;
ret = bdrv_truncate(c, 65536, PREALLOC_MODE_OFF, NULL);
ret = bdrv_truncate(c, 65536, false, PREALLOC_MODE_OFF, NULL);
g_assert_cmpint(ret, ==, -EACCES);
c->bs->read_only = false;

View file

@ -37,6 +37,7 @@ util-obj-y += rcu.o
util-obj-$(CONFIG_MEMBARRIER) += sys_membarrier.o
util-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
util-obj-y += qemu-coroutine-sleep.o
util-obj-y += qemu-co-shared-resource.o
util-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o
util-obj-y += buffer.o
util-obj-y += timed-average.o

View file

@ -387,6 +387,10 @@ void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count)
uint64_t first, n;
uint64_t last = start + count - 1;
if (count == 0) {
return;
}
trace_hbitmap_set(hb, start, count,
start >> hb->granularity, last >> hb->granularity);
@ -478,6 +482,10 @@ void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count)
uint64_t last = start + count - 1;
uint64_t gran = 1ULL << hb->granularity;
if (count == 0) {
return;
}
assert(QEMU_IS_ALIGNED(start, gran));
assert(QEMU_IS_ALIGNED(count, gran) || (start + count == hb->orig_size));

View file

@ -0,0 +1,76 @@
/*
* Helper functionality for distributing a fixed total amount of
* an abstract resource among multiple coroutines.
*
* Copyright (c) 2019 Virtuozzo International GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu/coroutine.h"
#include "qemu/co-shared-resource.h"
struct SharedResource {
uint64_t total;
uint64_t available;
CoQueue queue;
};
SharedResource *shres_create(uint64_t total)
{
SharedResource *s = g_new0(SharedResource, 1);
s->total = s->available = total;
qemu_co_queue_init(&s->queue);
return s;
}
void shres_destroy(SharedResource *s)
{
assert(s->available == s->total);
g_free(s);
}
bool co_try_get_from_shres(SharedResource *s, uint64_t n)
{
if (s->available >= n) {
s->available -= n;
return true;
}
return false;
}
void coroutine_fn co_get_from_shres(SharedResource *s, uint64_t n)
{
assert(n <= s->total);
while (!co_try_get_from_shres(s, n)) {
qemu_co_queue_wait(&s->queue, NULL);
}
}
void coroutine_fn co_put_to_shres(SharedResource *s, uint64_t n)
{
assert(s->total - s->available >= n);
s->available += n;
qemu_co_queue_restart_all(&s->queue);
}