aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2016-09-06 17:18:17 +0100
committerPeter Maydell <peter.maydell@linaro.org>2016-09-06 17:18:17 +0100
commit2926375cffce464fde6b4dabaed1e133d549af39 (patch)
treeb832cd53fd9a2f2a26fe5c70c4182266c780cc76
parentf9ae6bcf1d4659867cfc5e6d90a5e7fdad02280b (diff)
parente7f98f2f92827df9944402d1613a4e32fe50215b (diff)
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
Block layer patches # gpg: Signature made Tue 06 Sep 2016 11:38:01 BST # gpg: using RSA key 0x7F09B272C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * remotes/kevin/tags/for-upstream: (36 commits) block: Allow node name for 'qemu-io' HMP command qemu-iotests: Log QMP traffic in debug mode block jobs: Improve error message for missing job ID coroutine: Assert that no locks are held on termination coroutine: Let CoMutex remember who holds it qcow2: fix iovec size at qcow2_co_pwritev_compressed test-coroutine: Fix coroutine pool corruption qemu-iotests: add vmdk for test backup compression in 055 qemu-iotests: test backup compression in 055 blockdev-backup: added support for data compression drive-backup: added support for data compression block: simplify blockdev-backup block: simplify drive-backup block/io: turn on dirty_bitmaps for the compressed writes block: remove BlockDriver.bdrv_write_compressed qcow: cleanup qcow_co_pwritev_compressed to avoid the recursion qcow: add qcow_co_pwritev_compressed vmdk: add vmdk_co_pwritev_compressed qcow2: cleanup qcow2_co_pwritev_compressed to avoid the recursion qcow2: add qcow2_co_pwritev_compressed ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--block.c2
-rw-r--r--block/backup.c12
-rw-r--r--block/block-backend.c43
-rw-r--r--block/io.c48
-rw-r--r--block/qcow.c113
-rw-r--r--block/qcow2.c128
-rw-r--r--block/vmdk.c55
-rw-r--r--blockdev-nbd.c21
-rw-r--r--blockdev.c380
-rw-r--r--blockjob.c4
-rw-r--r--hmp-commands.hx8
-rw-r--r--hmp.c37
-rw-r--r--hw/ide/qdev.c20
-rw-r--r--hw/scsi/scsi-disk.c5
-rw-r--r--include/block/block.h5
-rw-r--r--include/block/block_int.h5
-rw-r--r--include/block/nbd.h3
-rw-r--r--include/qemu/coroutine.h1
-rw-r--r--include/qemu/coroutine_int.h1
-rw-r--r--include/sysemu/block-backend.h5
-rw-r--r--nbd/server.c25
-rw-r--r--qapi/block-core.json42
-rw-r--r--qapi/block.json14
-rw-r--r--qemu-img.c8
-rw-r--r--qemu-io-cmds.c2
-rw-r--r--qemu-nbd.c4
-rw-r--r--qmp-commands.hx34
-rwxr-xr-xtests/qemu-iotests/0302
-rwxr-xr-xtests/qemu-iotests/0418
-rwxr-xr-xtests/qemu-iotests/055125
-rw-r--r--tests/qemu-iotests/055.out4
-rwxr-xr-xtests/qemu-iotests/0574
-rw-r--r--tests/qemu-iotests/iotests.py15
-rw-r--r--tests/test-coroutine.c7
-rw-r--r--util/qemu-coroutine-lock.c14
-rw-r--r--util/qemu-coroutine.c1
36 files changed, 588 insertions, 617 deletions
diff --git a/block.c b/block.c
index 30d64e6ca5..101f8c628f 100644
--- a/block.c
+++ b/block.c
@@ -25,6 +25,7 @@
#include "trace.h"
#include "block/block_int.h"
#include "block/blockjob.h"
+#include "block/nbd.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "qapi/qmp/qerror.h"
@@ -2206,6 +2207,7 @@ static void bdrv_close(BlockDriverState *bs)
void bdrv_close_all(void)
{
block_job_cancel_sync_all();
+ nbd_export_close_all();
/* Drop references from requests still in flight, such as canceled block
* jobs whose AIO context has not been polled yet */
diff --git a/block/backup.c b/block/backup.c
index 2c0532314f..bb3bb9a9eb 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -47,6 +47,7 @@ typedef struct BackupBlockJob {
uint64_t sectors_read;
unsigned long *done_bitmap;
int64_t cluster_size;
+ bool compress;
NotifierWithReturn before_write;
QLIST_HEAD(, CowRequest) inflight_reqs;
} BackupBlockJob;
@@ -154,7 +155,8 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
} else {
ret = blk_co_pwritev(job->target, start * job->cluster_size,
- bounce_qiov.size, &bounce_qiov, 0);
+ bounce_qiov.size, &bounce_qiov,
+ job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
}
if (ret < 0) {
trace_backup_do_cow_write_fail(job, start, ret);
@@ -477,6 +479,7 @@ static void coroutine_fn backup_run(void *opaque)
void backup_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *target, int64_t speed,
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
+ bool compress,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
BlockCompletionFunc *cb, void *opaque,
@@ -507,6 +510,12 @@ void backup_start(const char *job_id, BlockDriverState *bs,
return;
}
+ if (compress && target->drv->bdrv_co_pwritev_compressed == NULL) {
+ error_setg(errp, "Compression is not supported for this drive %s",
+ bdrv_get_device_name(target));
+ return;
+ }
+
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
return;
}
@@ -555,6 +564,7 @@ void backup_start(const char *job_id, BlockDriverState *bs,
job->sync_mode = sync_mode;
job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ?
sync_bitmap : NULL;
+ job->compress = compress;
/* If there is no backing file on the target, we cannot rely on COW if our
* backup cluster size is smaller than the target cluster size. Even for
diff --git a/block/block-backend.c b/block/block-backend.c
index effa038924..d1349d90e5 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -410,6 +410,22 @@ bool bdrv_has_blk(BlockDriverState *bs)
}
/*
+ * Returns true if @bs has only BlockBackends as parents.
+ */
+bool bdrv_is_root_node(BlockDriverState *bs)
+{
+ BdrvChild *c;
+
+ QLIST_FOREACH(c, &bs->parents, next_parent) {
+ if (c->role != &child_root) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
* Return @blk's DriveInfo if any, else null.
*/
DriveInfo *blk_legacy_dinfo(BlockBackend *blk)
@@ -727,21 +743,6 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
return 0;
}
-static int blk_check_request(BlockBackend *blk, int64_t sector_num,
- int nb_sectors)
-{
- if (sector_num < 0 || sector_num > INT64_MAX / BDRV_SECTOR_SIZE) {
- return -EIO;
- }
-
- if (nb_sectors < 0 || nb_sectors > INT_MAX / BDRV_SECTOR_SIZE) {
- return -EIO;
- }
-
- return blk_check_byte_request(blk, sector_num * BDRV_SECTOR_SIZE,
- nb_sectors * BDRV_SECTOR_SIZE);
-}
-
int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags)
@@ -1484,15 +1485,11 @@ int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset,
flags | BDRV_REQ_ZERO_WRITE);
}
-int blk_write_compressed(BlockBackend *blk, int64_t sector_num,
- const uint8_t *buf, int nb_sectors)
+int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf,
+ int count)
{
- int ret = blk_check_request(blk, sector_num, nb_sectors);
- if (ret < 0) {
- return ret;
- }
-
- return bdrv_write_compressed(blk_bs(blk), sector_num, buf, nb_sectors);
+ return blk_prw(blk, offset, (void *) buf, count, blk_write_entry,
+ BDRV_REQ_WRITE_COMPRESSED);
}
int blk_truncate(BlockBackend *blk, int64_t offset)
diff --git a/block/io.c b/block/io.c
index 420944d80d..fdf70807b0 100644
--- a/block/io.c
+++ b/block/io.c
@@ -540,17 +540,6 @@ static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
return 0;
}
-static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors)
-{
- if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
- return -EIO;
- }
-
- return bdrv_check_byte_request(bs, sector_num * BDRV_SECTOR_SIZE,
- nb_sectors * BDRV_SECTOR_SIZE);
-}
-
typedef struct RwCo {
BdrvChild *child;
int64_t offset;
@@ -897,6 +886,19 @@ emulate_flags:
return ret;
}
+static int coroutine_fn
+bdrv_driver_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, QEMUIOVector *qiov)
+{
+ BlockDriver *drv = bs->drv;
+
+ if (!drv->bdrv_co_pwritev_compressed) {
+ return -ENOTSUP;
+ }
+
+ return drv->bdrv_co_pwritev_compressed(bs, offset, bytes, qiov);
+}
+
static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs,
int64_t offset, unsigned int bytes, QEMUIOVector *qiov)
{
@@ -1315,6 +1317,8 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
} else if (flags & BDRV_REQ_ZERO_WRITE) {
bdrv_debug_event(bs, BLKDBG_PWRITEV_ZERO);
ret = bdrv_co_do_pwrite_zeroes(bs, offset, bytes, flags);
+ } else if (flags & BDRV_REQ_WRITE_COMPRESSED) {
+ ret = bdrv_driver_pwritev_compressed(bs, offset, bytes, qiov);
} else if (bytes <= max_transfer) {
bdrv_debug_event(bs, BLKDBG_PWRITEV);
ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, flags);
@@ -1879,28 +1883,6 @@ int bdrv_is_allocated_above(BlockDriverState *top,
return 0;
}
-int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
- const uint8_t *buf, int nb_sectors)
-{
- BlockDriver *drv = bs->drv;
- int ret;
-
- if (!drv) {
- return -ENOMEDIUM;
- }
- if (!drv->bdrv_write_compressed) {
- return -ENOTSUP;
- }
- ret = bdrv_check_request(bs, sector_num, nb_sectors);
- if (ret < 0) {
- return ret;
- }
-
- assert(QLIST_EMPTY(&bs->dirty_bitmaps));
-
- return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors);
-}
-
typedef struct BdrvVmstateCo {
BlockDriverState *bs;
QEMUIOVector *qiov;
diff --git a/block/qcow.c b/block/qcow.c
index 6f9b2e2d26..94f01b3d0c 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -913,75 +913,32 @@ static int qcow_make_empty(BlockDriverState *bs)
return 0;
}
-typedef struct QcowWriteCo {
- BlockDriverState *bs;
- int64_t sector_num;
- const uint8_t *buf;
- int nb_sectors;
- int ret;
-} QcowWriteCo;
-
-static void qcow_write_co_entry(void *opaque)
-{
- QcowWriteCo *co = opaque;
- QEMUIOVector qiov;
-
- struct iovec iov = (struct iovec) {
- .iov_base = (uint8_t*) co->buf,
- .iov_len = co->nb_sectors * BDRV_SECTOR_SIZE,
- };
- qemu_iovec_init_external(&qiov, &iov, 1);
-
- co->ret = qcow_co_writev(co->bs, co->sector_num, co->nb_sectors, &qiov);
-}
-
-/* Wrapper for non-coroutine contexts */
-static int qcow_write(BlockDriverState *bs, int64_t sector_num,
- const uint8_t *buf, int nb_sectors)
-{
- Coroutine *co;
- AioContext *aio_context = bdrv_get_aio_context(bs);
- QcowWriteCo data = {
- .bs = bs,
- .sector_num = sector_num,
- .buf = buf,
- .nb_sectors = nb_sectors,
- .ret = -EINPROGRESS,
- };
- co = qemu_coroutine_create(qcow_write_co_entry, &data);
- qemu_coroutine_enter(co);
- while (data.ret == -EINPROGRESS) {
- aio_poll(aio_context, true);
- }
- return data.ret;
-}
-
/* XXX: put compressed sectors first, then all the cluster aligned
tables to avoid losing bytes in alignment */
-static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
- const uint8_t *buf, int nb_sectors)
+static coroutine_fn int
+qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, QEMUIOVector *qiov)
{
BDRVQcowState *s = bs->opaque;
+ QEMUIOVector hd_qiov;
+ struct iovec iov;
z_stream strm;
int ret, out_len;
- uint8_t *out_buf;
+ uint8_t *buf, *out_buf;
uint64_t cluster_offset;
- if (nb_sectors != s->cluster_sectors) {
- ret = -EINVAL;
-
- /* Zero-pad last write if image size is not cluster aligned */
- if (sector_num + nb_sectors == bs->total_sectors &&
- nb_sectors < s->cluster_sectors) {
- uint8_t *pad_buf = qemu_blockalign(bs, s->cluster_size);
- memset(pad_buf, 0, s->cluster_size);
- memcpy(pad_buf, buf, nb_sectors * BDRV_SECTOR_SIZE);
- ret = qcow_write_compressed(bs, sector_num,
- pad_buf, s->cluster_sectors);
- qemu_vfree(pad_buf);
+ buf = qemu_blockalign(bs, s->cluster_size);
+ if (bytes != s->cluster_size) {
+ if (bytes > s->cluster_size ||
+ offset + bytes != bs->total_sectors << BDRV_SECTOR_BITS)
+ {
+ qemu_vfree(buf);
+ return -EINVAL;
}
- return ret;
+ /* Zero-pad last write if image size is not cluster aligned */
+ memset(buf + bytes, 0, s->cluster_size - bytes);
}
+ qemu_iovec_to_buf(qiov, 0, buf, qiov->size);
out_buf = g_malloc(s->cluster_size);
@@ -1012,27 +969,35 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
/* could not compress: write normal cluster */
- ret = qcow_write(bs, sector_num, buf, s->cluster_sectors);
- if (ret < 0) {
- goto fail;
- }
- } else {
- cluster_offset = get_cluster_offset(bs, sector_num << 9, 2,
- out_len, 0, 0);
- if (cluster_offset == 0) {
- ret = -EIO;
- goto fail;
- }
-
- cluster_offset &= s->cluster_offset_mask;
- ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len);
+ ret = qcow_co_writev(bs, offset >> BDRV_SECTOR_BITS,
+ bytes >> BDRV_SECTOR_BITS, qiov);
if (ret < 0) {
goto fail;
}
+ goto success;
}
+ qemu_co_mutex_lock(&s->lock);
+ cluster_offset = get_cluster_offset(bs, offset, 2, out_len, 0, 0);
+ qemu_co_mutex_unlock(&s->lock);
+ if (cluster_offset == 0) {
+ ret = -EIO;
+ goto fail;
+ }
+ cluster_offset &= s->cluster_offset_mask;
+ iov = (struct iovec) {
+ .iov_base = out_buf,
+ .iov_len = out_len,
+ };
+ qemu_iovec_init_external(&hd_qiov, &iov, 1);
+ ret = bdrv_co_pwritev(bs->file, cluster_offset, out_len, &hd_qiov, 0);
+ if (ret < 0) {
+ goto fail;
+ }
+success:
ret = 0;
fail:
+ qemu_vfree(buf);
g_free(out_buf);
return ret;
}
@@ -1085,7 +1050,7 @@ static BlockDriver bdrv_qcow = {
.bdrv_set_key = qcow_set_key,
.bdrv_make_empty = qcow_make_empty,
- .bdrv_write_compressed = qcow_write_compressed,
+ .bdrv_co_pwritev_compressed = qcow_co_pwritev_compressed,
.bdrv_get_info = qcow_get_info,
.create_opts = &qcow_create_opts,
diff --git a/block/qcow2.c b/block/qcow2.c
index 91ef4dfefc..c079aa83b6 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2533,84 +2533,39 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset)
return 0;
}
-typedef struct Qcow2WriteCo {
- BlockDriverState *bs;
- int64_t sector_num;
- const uint8_t *buf;
- int nb_sectors;
- int ret;
-} Qcow2WriteCo;
-
-static void qcow2_write_co_entry(void *opaque)
-{
- Qcow2WriteCo *co = opaque;
- QEMUIOVector qiov;
- uint64_t offset = co->sector_num * BDRV_SECTOR_SIZE;
- uint64_t bytes = co->nb_sectors * BDRV_SECTOR_SIZE;
-
- struct iovec iov = (struct iovec) {
- .iov_base = (uint8_t*) co->buf,
- .iov_len = bytes,
- };
- qemu_iovec_init_external(&qiov, &iov, 1);
-
- co->ret = qcow2_co_pwritev(co->bs, offset, bytes, &qiov, 0);
-}
-
-/* Wrapper for non-coroutine contexts */
-static int qcow2_write(BlockDriverState *bs, int64_t sector_num,
- const uint8_t *buf, int nb_sectors)
-{
- Coroutine *co;
- AioContext *aio_context = bdrv_get_aio_context(bs);
- Qcow2WriteCo data = {
- .bs = bs,
- .sector_num = sector_num,
- .buf = buf,
- .nb_sectors = nb_sectors,
- .ret = -EINPROGRESS,
- };
- co = qemu_coroutine_create(qcow2_write_co_entry, &data);
- qemu_coroutine_enter(co);
- while (data.ret == -EINPROGRESS) {
- aio_poll(aio_context, true);
- }
- return data.ret;
-}
-
/* XXX: put compressed sectors first, then all the cluster aligned
tables to avoid losing bytes in alignment */
-static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
- const uint8_t *buf, int nb_sectors)
+static coroutine_fn int
+qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, QEMUIOVector *qiov)
{
BDRVQcow2State *s = bs->opaque;
+ QEMUIOVector hd_qiov;
+ struct iovec iov;
z_stream strm;
int ret, out_len;
- uint8_t *out_buf;
+ uint8_t *buf, *out_buf;
uint64_t cluster_offset;
- if (nb_sectors == 0) {
+ if (bytes == 0) {
/* align end of file to a sector boundary to ease reading with
sector based I/Os */
cluster_offset = bdrv_getlength(bs->file->bs);
return bdrv_truncate(bs->file->bs, cluster_offset);
}
- if (nb_sectors != s->cluster_sectors) {
- ret = -EINVAL;
-
- /* Zero-pad last write if image size is not cluster aligned */
- if (sector_num + nb_sectors == bs->total_sectors &&
- nb_sectors < s->cluster_sectors) {
- uint8_t *pad_buf = qemu_blockalign(bs, s->cluster_size);
- memset(pad_buf, 0, s->cluster_size);
- memcpy(pad_buf, buf, nb_sectors * BDRV_SECTOR_SIZE);
- ret = qcow2_write_compressed(bs, sector_num,
- pad_buf, s->cluster_sectors);
- qemu_vfree(pad_buf);
+ buf = qemu_blockalign(bs, s->cluster_size);
+ if (bytes != s->cluster_size) {
+ if (bytes > s->cluster_size ||
+ offset + bytes != bs->total_sectors << BDRV_SECTOR_BITS)
+ {
+ qemu_vfree(buf);
+ return -EINVAL;
}
- return ret;
+ /* Zero-pad last write if image size is not cluster aligned */
+ memset(buf + bytes, 0, s->cluster_size - bytes);
}
+ qemu_iovec_to_buf(qiov, 0, buf, bytes);
out_buf = g_malloc(s->cluster_size);
@@ -2641,33 +2596,44 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
/* could not compress: write normal cluster */
- ret = qcow2_write(bs, sector_num, buf, s->cluster_sectors);
+ ret = qcow2_co_pwritev(bs, offset, bytes, qiov, 0);
if (ret < 0) {
goto fail;
}
- } else {
- cluster_offset = qcow2_alloc_compressed_cluster_offset(bs,
- sector_num << 9, out_len);
- if (!cluster_offset) {
- ret = -EIO;
- goto fail;
- }
- cluster_offset &= s->cluster_offset_mask;
+ goto success;
+ }
- ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len);
- if (ret < 0) {
- goto fail;
- }
+ qemu_co_mutex_lock(&s->lock);
+ cluster_offset =
+ qcow2_alloc_compressed_cluster_offset(bs, offset, out_len);
+ if (!cluster_offset) {
+ qemu_co_mutex_unlock(&s->lock);
+ ret = -EIO;
+ goto fail;
+ }
+ cluster_offset &= s->cluster_offset_mask;
- BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
- ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len);
- if (ret < 0) {
- goto fail;
- }
+ ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len);
+ qemu_co_mutex_unlock(&s->lock);
+ if (ret < 0) {
+ goto fail;
}
+ iov = (struct iovec) {
+ .iov_base = out_buf,
+ .iov_len = out_len,
+ };
+ qemu_iovec_init_external(&hd_qiov, &iov, 1);
+
+ BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
+ ret = bdrv_co_pwritev(bs->file, cluster_offset, out_len, &hd_qiov, 0);
+ if (ret < 0) {
+ goto fail;
+ }
+success:
ret = 0;
fail:
+ qemu_vfree(buf);
g_free(out_buf);
return ret;
}
@@ -3412,7 +3378,7 @@ BlockDriver bdrv_qcow2 = {
.bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes,
.bdrv_co_pdiscard = qcow2_co_pdiscard,
.bdrv_truncate = qcow2_truncate,
- .bdrv_write_compressed = qcow2_write_compressed,
+ .bdrv_co_pwritev_compressed = qcow2_co_pwritev_compressed,
.bdrv_make_empty = qcow2_make_empty,
.bdrv_snapshot_create = qcow2_snapshot_create,
diff --git a/block/vmdk.c b/block/vmdk.c
index 46d474e442..a11c27a1c4 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -1645,56 +1645,11 @@ vmdk_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
return ret;
}
-typedef struct VmdkWriteCompressedCo {
- BlockDriverState *bs;
- int64_t sector_num;
- const uint8_t *buf;
- int nb_sectors;
- int ret;
-} VmdkWriteCompressedCo;
-
-static void vmdk_co_write_compressed(void *opaque)
-{
- VmdkWriteCompressedCo *co = opaque;
- QEMUIOVector local_qiov;
- uint64_t offset = co->sector_num * BDRV_SECTOR_SIZE;
- uint64_t bytes = co->nb_sectors * BDRV_SECTOR_SIZE;
-
- struct iovec iov = (struct iovec) {
- .iov_base = (uint8_t*) co->buf,
- .iov_len = bytes,
- };
- qemu_iovec_init_external(&local_qiov, &iov, 1);
-
- co->ret = vmdk_pwritev(co->bs, offset, bytes, &local_qiov, false, false);
-}
-
-static int vmdk_write_compressed(BlockDriverState *bs,
- int64_t sector_num,
- const uint8_t *buf,
- int nb_sectors)
+static int coroutine_fn
+vmdk_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, QEMUIOVector *qiov)
{
- BDRVVmdkState *s = bs->opaque;
-
- if (s->num_extents == 1 && s->extents[0].compressed) {
- Coroutine *co;
- AioContext *aio_context = bdrv_get_aio_context(bs);
- VmdkWriteCompressedCo data = {
- .bs = bs,
- .sector_num = sector_num,
- .buf = buf,
- .nb_sectors = nb_sectors,
- .ret = -EINPROGRESS,
- };
- co = qemu_coroutine_create(vmdk_co_write_compressed, &data);
- qemu_coroutine_enter(co);
- while (data.ret == -EINPROGRESS) {
- aio_poll(aio_context, true);
- }
- return data.ret;
- } else {
- return -ENOTSUP;
- }
+ return vmdk_co_pwritev(bs, offset, bytes, qiov, 0);
}
static int coroutine_fn vmdk_co_pwrite_zeroes(BlockDriverState *bs,
@@ -2393,7 +2348,7 @@ static BlockDriver bdrv_vmdk = {
.bdrv_reopen_prepare = vmdk_reopen_prepare,
.bdrv_co_preadv = vmdk_co_preadv,
.bdrv_co_pwritev = vmdk_co_pwritev,
- .bdrv_write_compressed = vmdk_write_compressed,
+ .bdrv_co_pwritev_compressed = vmdk_co_pwritev_compressed,
.bdrv_co_pwrite_zeroes = vmdk_co_pwrite_zeroes,
.bdrv_close = vmdk_close,
.bdrv_create = vmdk_create,
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 12cae0ea72..ca41cc6fdd 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -145,7 +145,8 @@ void qmp_nbd_server_start(SocketAddress *addr,
void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
Error **errp)
{
- BlockBackend *blk;
+ BlockDriverState *bs = NULL;
+ BlockBackend *on_eject_blk;
NBDExport *exp;
if (!nbd_server) {
@@ -158,26 +159,22 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
return;
}
- blk = blk_by_name(device);
- if (!blk) {
- error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
- "Device '%s' not found", device);
- return;
- }
- if (!blk_is_inserted(blk)) {
- error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
+ on_eject_blk = blk_by_name(device);
+
+ bs = bdrv_lookup_bs(device, device, errp);
+ if (!bs) {
return;
}
if (!has_writable) {
writable = false;
}
- if (blk_is_read_only(blk)) {
+ if (bdrv_is_read_only(bs)) {
writable = false;
}
- exp = nbd_export_new(blk, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, NULL,
- errp);
+ exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY,
+ NULL, false, on_eject_blk, errp);
if (!exp) {
return;
}
diff --git a/blockdev.c b/blockdev.c
index 21614004d1..97062e33c3 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1174,6 +1174,28 @@ fail:
return dinfo;
}
+static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp)
+{
+ BlockDriverState *bs;
+
+ bs = bdrv_lookup_bs(name, name, errp);
+ if (bs == NULL) {
+ return NULL;
+ }
+
+ if (!bdrv_is_root_node(bs)) {
+ error_setg(errp, "Need a root block node");
+ return NULL;
+ }
+
+ if (!bdrv_is_inserted(bs)) {
+ error_setg(errp, "Device has no medium");
+ return NULL;
+ }
+
+ return bs;
+}
+
void hmp_commit(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
@@ -1284,21 +1306,17 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
Error **errp)
{
BlockDriverState *bs;
- BlockBackend *blk;
AioContext *aio_context;
QEMUSnapshotInfo sn;
Error *local_err = NULL;
SnapshotInfo *info = NULL;
int ret;
- blk = blk_by_name(device);
- if (!blk) {
- error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
- "Device '%s' not found", device);
+ bs = qmp_get_root_bs(device, errp);
+ if (!bs) {
return NULL;
}
-
- aio_context = blk_get_aio_context(blk);
+ aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
if (!has_id) {
@@ -1314,12 +1332,6 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
goto out_aio_context;
}
- if (!blk_is_available(blk)) {
- error_setg(errp, "Device '%s' has no medium", device);
- goto out_aio_context;
- }
- bs = blk_bs(blk);
-
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) {
goto out_aio_context;
}
@@ -1499,7 +1511,6 @@ static void internal_snapshot_prepare(BlkActionState *common,
Error *local_err = NULL;
const char *device;
const char *name;
- BlockBackend *blk;
BlockDriverState *bs;
QEMUSnapshotInfo old_sn, *sn;
bool ret;
@@ -1522,23 +1533,15 @@ static void internal_snapshot_prepare(BlkActionState *common,
return;
}
- blk = blk_by_name(device);
- if (!blk) {
- error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
- "Device '%s' not found", device);
+ bs = qmp_get_root_bs(device, errp);
+ if (!bs) {
return;
}
/* AioContext is released in .clean() */
- state->aio_context = blk_get_aio_context(blk);
+ state->aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(state->aio_context);
- if (!blk_is_available(blk)) {
- error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
- return;
- }
- bs = blk_bs(blk);
-
state->bs = bs;
bdrv_drained_begin(bs);
@@ -1838,56 +1841,31 @@ typedef struct DriveBackupState {
BlockJob *job;
} DriveBackupState;
-static void do_drive_backup(const char *job_id, const char *device,
- const char *target, bool has_format,
- const char *format, enum MirrorSyncMode sync,
- bool has_mode, enum NewImageMode mode,
- bool has_speed, int64_t speed,
- bool has_bitmap, const char *bitmap,
- bool has_on_source_error,
- BlockdevOnError on_source_error,
- bool has_on_target_error,
- BlockdevOnError on_target_error,
- BlockJobTxn *txn, Error **errp);
+static void do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
+ Error **errp);
static void drive_backup_prepare(BlkActionState *common, Error **errp)
{
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
- BlockBackend *blk;
+ BlockDriverState *bs;
DriveBackup *backup;
Error *local_err = NULL;
assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
backup = common->action->u.drive_backup.data;
- blk = blk_by_name(backup->device);
- if (!blk) {
- error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
- "Device '%s' not found", backup->device);
- return;
- }
-
- if (!blk_is_available(blk)) {
- error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, backup->device);
+ bs = qmp_get_root_bs(backup->device, errp);
+ if (!bs) {
return;
}
/* AioContext is released in .clean() */
- state->aio_context = blk_get_aio_context(blk);
+ state->aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(state->aio_context);
- bdrv_drained_begin(blk_bs(blk));
- state->bs = blk_bs(blk);
-
- do_drive_backup(backup->has_job_id ? backup->job_id : NULL,
- backup->device, backup->target,
- backup->has_format, backup->format,
- backup->sync,
- backup->has_mode, backup->mode,
- backup->has_speed, backup->speed,
- backup->has_bitmap, backup->bitmap,
- backup->has_on_source_error, backup->on_source_error,
- backup->has_on_target_error, backup->on_target_error,
- common->block_job_txn, &local_err);
+ bdrv_drained_begin(bs);
+ state->bs = bs;
+
+ do_drive_backup(backup, common->block_job_txn, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -1924,34 +1902,21 @@ typedef struct BlockdevBackupState {
AioContext *aio_context;
} BlockdevBackupState;
-static void do_blockdev_backup(const char *job_id, const char *device,
- const char *target, enum MirrorSyncMode sync,
- bool has_speed, int64_t speed,
- bool has_on_source_error,
- BlockdevOnError on_source_error,
- bool has_on_target_error,
- BlockdevOnError on_target_error,
- BlockJobTxn *txn, Error **errp);
+static void do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
+ Error **errp);
static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
{
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
BlockdevBackup *backup;
- BlockBackend *blk;
- BlockDriverState *target;
+ BlockDriverState *bs, *target;
Error *local_err = NULL;
assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
backup = common->action->u.blockdev_backup.data;
- blk = blk_by_name(backup->device);
- if (!blk) {
- error_setg(errp, "Device '%s' not found", backup->device);
- return;
- }
-
- if (!blk_is_available(blk)) {
- error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, backup->device);
+ bs = qmp_get_root_bs(backup->device, errp);
+ if (!bs) {
return;
}
@@ -1961,22 +1926,17 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
}
/* AioContext is released in .clean() */
- state->aio_context = blk_get_aio_context(blk);
+ state->aio_context = bdrv_get_aio_context(bs);
if (state->aio_context != bdrv_get_aio_context(target)) {
state->aio_context = NULL;
error_setg(errp, "Backup between two IO threads is not implemented");
return;
}
aio_context_acquire(state->aio_context);
- state->bs = blk_bs(blk);
+ state->bs = bs;
bdrv_drained_begin(state->bs);
- do_blockdev_backup(backup->has_job_id ? backup->job_id : NULL,
- backup->device, backup->target, backup->sync,
- backup->has_speed, backup->speed,
- backup->has_on_source_error, backup->on_source_error,
- backup->has_on_target_error, backup->on_target_error,
- common->block_job_txn, &local_err);
+ do_blockdev_backup(backup, common->block_job_txn, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -2983,7 +2943,6 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
bool has_on_error, BlockdevOnError on_error,
Error **errp)
{
- BlockBackend *blk;
BlockDriverState *bs;
BlockDriverState *base_bs = NULL;
AioContext *aio_context;
@@ -2994,22 +2953,14 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
on_error = BLOCKDEV_ON_ERROR_REPORT;
}
- blk = blk_by_name(device);
- if (!blk) {
- error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
- "Device '%s' not found", device);
+ bs = qmp_get_root_bs(device, errp);
+ if (!bs) {
return;
}
- aio_context = blk_get_aio_context(blk);
+ aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- if (!blk_is_available(blk)) {
- error_setg(errp, "Device '%s' has no medium", device);
- goto out;
- }
- bs = blk_bs(blk);
-
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) {
goto out;
}
@@ -3055,7 +3006,6 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
bool has_speed, int64_t speed,
Error **errp)
{
- BlockBackend *blk;
BlockDriverState *bs;
BlockDriverState *base_bs, *top_bs;
AioContext *aio_context;
@@ -3074,22 +3024,22 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
* live commit feature versions; for this to work, we must make sure to
* perform the device lookup before any generic errors that may occur in a
* scenario in which all optional arguments are omitted. */
- blk = blk_by_name(device);
- if (!blk) {
- error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
- "Device '%s' not found", device);
+ bs = qmp_get_root_bs(device, &local_err);
+ if (!bs) {
+ bs = bdrv_lookup_bs(device, device, NULL);
+ if (!bs) {
+ error_free(local_err);
+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+ "Device '%s' not found", device);
+ } else {
+ error_propagate(errp, local_err);
+ }
return;
}
- aio_context = blk_get_aio_context(blk);
+ aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- if (!blk_is_available(blk)) {
- error_setg(errp, "Device '%s' has no medium", device);
- goto out;
- }
- bs = blk_bs(blk);
-
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, errp)) {
goto out;
}
@@ -3155,19 +3105,8 @@ out:
aio_context_release(aio_context);
}
-static void do_drive_backup(const char *job_id, const char *device,
- const char *target, bool has_format,
- const char *format, enum MirrorSyncMode sync,
- bool has_mode, enum NewImageMode mode,
- bool has_speed, int64_t speed,
- bool has_bitmap, const char *bitmap,
- bool has_on_source_error,
- BlockdevOnError on_source_error,
- bool has_on_target_error,
- BlockdevOnError on_target_error,
- BlockJobTxn *txn, Error **errp)
+static void do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, Error **errp)
{
- BlockBackend *blk;
BlockDriverState *bs;
BlockDriverState *target_bs;
BlockDriverState *source = NULL;
@@ -3178,39 +3117,36 @@ static void do_drive_backup(const char *job_id, const char *device,
int flags;
int64_t size;
- if (!has_speed) {
- speed = 0;
+ if (!backup->has_speed) {
+ backup->speed = 0;
}
- if (!has_on_source_error) {
- on_source_error = BLOCKDEV_ON_ERROR_REPORT;
+ if (!backup->has_on_source_error) {
+ backup->on_source_error = BLOCKDEV_ON_ERROR_REPORT;
}
- if (!has_on_target_error) {
- on_target_error = BLOCKDEV_ON_ERROR_REPORT;
+ if (!backup->has_on_target_error) {
+ backup->on_target_error = BLOCKDEV_ON_ERROR_REPORT;
+ }
+ if (!backup->has_mode) {
+ backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
}
- if (!has_mode) {
- mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
+ if (!backup->has_job_id) {
+ backup->job_id = NULL;
+ }
+ if (!backup->has_compress) {
+ backup->compress = false;
}
- blk = blk_by_name(device);
- if (!blk) {
- error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
- "Device '%s' not found", device);
+ bs = qmp_get_root_bs(backup->device, errp);
+ if (!bs) {
return;
}
- aio_context = blk_get_aio_context(blk);
+ aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- /* Although backup_run has this check too, we need to use bs->drv below, so
- * do an early check redundantly. */
- if (!blk_is_available(blk)) {
- error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
- goto out;
- }
- bs = blk_bs(blk);
-
- if (!has_format) {
- format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
+ if (!backup->has_format) {
+ backup->format = backup->mode == NEW_IMAGE_MODE_EXISTING ?
+ NULL : (char*) bs->drv->format_name;
}
/* Early check to avoid creating target */
@@ -3222,13 +3158,13 @@ static void do_drive_backup(const char *job_id, const char *device,
/* See if we have a backing HD we can use to create our new image
* on top of. */
- if (sync == MIRROR_SYNC_MODE_TOP) {
+ if (backup->sync == MIRROR_SYNC_MODE_TOP) {
source = backing_bs(bs);
if (!source) {
- sync = MIRROR_SYNC_MODE_FULL;
+ backup->sync = MIRROR_SYNC_MODE_FULL;
}
}
- if (sync == MIRROR_SYNC_MODE_NONE) {
+ if (backup->sync == MIRROR_SYNC_MODE_NONE) {
source = bs;
}
@@ -3238,14 +3174,14 @@ static void do_drive_backup(const char *job_id, const char *device,
goto out;
}
- if (mode != NEW_IMAGE_MODE_EXISTING) {
- assert(format);
+ if (backup->mode != NEW_IMAGE_MODE_EXISTING) {
+ assert(backup->format);
if (source) {
- bdrv_img_create(target, format, source->filename,
+ bdrv_img_create(backup->target, backup->format, source->filename,
source->drv->format_name, NULL,
size, flags, &local_err, false);
} else {
- bdrv_img_create(target, format, NULL, NULL, NULL,
+ bdrv_img_create(backup->target, backup->format, NULL, NULL, NULL,
size, flags, &local_err, false);
}
}
@@ -3255,30 +3191,30 @@ static void do_drive_backup(const char *job_id, const char *device,
goto out;
}
- if (format) {
+ if (backup->format) {
options = qdict_new();
- qdict_put(options, "driver", qstring_from_str(format));
+ qdict_put(options, "driver", qstring_from_str(backup->format));
}
- target_bs = bdrv_open(target, NULL, options, flags, errp);
+ target_bs = bdrv_open(backup->target, NULL, options, flags, errp);
if (!target_bs) {
goto out;
}
bdrv_set_aio_context(target_bs, aio_context);
- if (has_bitmap) {
- bmap = bdrv_find_dirty_bitmap(bs, bitmap);
+ if (backup->has_bitmap) {
+ bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
if (!bmap) {
- error_setg(errp, "Bitmap '%s' could not be found", bitmap);
+ error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap);
bdrv_unref(target_bs);
goto out;
}
}
- backup_start(job_id, bs, target_bs, speed, sync, bmap,
- on_source_error, on_target_error,
- block_job_cb, bs, txn, &local_err);
+ backup_start(backup->job_id, bs, target_bs, backup->speed, backup->sync,
+ bmap, backup->compress, backup->on_source_error,
+ backup->on_target_error, block_job_cb, bs, txn, &local_err);
bdrv_unref(target_bs);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -3289,24 +3225,9 @@ out:
aio_context_release(aio_context);
}
-void qmp_drive_backup(bool has_job_id, const char *job_id,
- const char *device, const char *target,
- bool has_format, const char *format,
- enum MirrorSyncMode sync,
- bool has_mode, enum NewImageMode mode,
- bool has_speed, int64_t speed,
- bool has_bitmap, const char *bitmap,
- bool has_on_source_error, BlockdevOnError on_source_error,
- bool has_on_target_error, BlockdevOnError on_target_error,
- Error **errp)
+void qmp_drive_backup(DriveBackup *arg, Error **errp)
{
- return do_drive_backup(has_job_id ? job_id : NULL, device, target,
- has_format, format, sync,
- has_mode, mode, has_speed, speed,
- has_bitmap, bitmap,
- has_on_source_error, on_source_error,
- has_on_target_error, on_target_error,
- NULL, errp);
+ return do_drive_backup(arg, NULL, errp);
}
BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
@@ -3314,47 +3235,38 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
return bdrv_named_nodes_list(errp);
}
-void do_blockdev_backup(const char *job_id, const char *device,
- const char *target, enum MirrorSyncMode sync,
- bool has_speed, int64_t speed,
- bool has_on_source_error,
- BlockdevOnError on_source_error,
- bool has_on_target_error,
- BlockdevOnError on_target_error,
- BlockJobTxn *txn, Error **errp)
+void do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, Error **errp)
{
- BlockBackend *blk;
BlockDriverState *bs;
BlockDriverState *target_bs;
Error *local_err = NULL;
AioContext *aio_context;
- if (!has_speed) {
- speed = 0;
+ if (!backup->has_speed) {
+ backup->speed = 0;
}
- if (!has_on_source_error) {
- on_source_error = BLOCKDEV_ON_ERROR_REPORT;
+ if (!backup->has_on_source_error) {
+ backup->on_source_error = BLOCKDEV_ON_ERROR_REPORT;
}
- if (!has_on_target_error) {
- on_target_error = BLOCKDEV_ON_ERROR_REPORT;
+ if (!backup->has_on_target_error) {
+ backup->on_target_error = BLOCKDEV_ON_ERROR_REPORT;
+ }
+ if (!backup->has_job_id) {
+ backup->job_id = NULL;
+ }
+ if (!backup->has_compress) {
+ backup->compress = false;
}
- blk = blk_by_name(device);
- if (!blk) {
- error_setg(errp, "Device '%s' not found", device);
+ bs = qmp_get_root_bs(backup->device, errp);
+ if (!bs) {
return;
}
- aio_context = blk_get_aio_context(blk);
+ aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- if (!blk_is_available(blk)) {
- error_setg(errp, "Device '%s' has no medium", device);
- goto out;
- }
- bs = blk_bs(blk);
-
- target_bs = bdrv_lookup_bs(target, target, errp);
+ target_bs = bdrv_lookup_bs(backup->target, backup->target, errp);
if (!target_bs) {
goto out;
}
@@ -3370,8 +3282,9 @@ void do_blockdev_backup(const char *job_id, const char *device,
goto out;
}
}
- backup_start(job_id, bs, target_bs, speed, sync, NULL, on_source_error,
- on_target_error, block_job_cb, bs, txn, &local_err);
+ backup_start(backup->job_id, bs, target_bs, backup->speed, backup->sync,
+ NULL, backup->compress, backup->on_source_error,
+ backup->on_target_error, block_job_cb, bs, txn, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
}
@@ -3379,21 +3292,9 @@ out:
aio_context_release(aio_context);
}
-void qmp_blockdev_backup(bool has_job_id, const char *job_id,
- const char *device, const char *target,
- enum MirrorSyncMode sync,
- bool has_speed, int64_t speed,
- bool has_on_source_error,
- BlockdevOnError on_source_error,
- bool has_on_target_error,
- BlockdevOnError on_target_error,
- Error **errp)
+void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp)
{
- do_blockdev_backup(has_job_id ? job_id : NULL, device, target,
- sync, has_speed, speed,
- has_on_source_error, on_source_error,
- has_on_target_error, on_target_error,
- NULL, errp);
+ do_blockdev_backup(arg, NULL, errp);
}
/* Parameter check and block job starting for drive mirroring.
@@ -3469,7 +3370,6 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
void qmp_drive_mirror(DriveMirror *arg, Error **errp)
{
BlockDriverState *bs;
- BlockBackend *blk;
BlockDriverState *source, *target_bs;
AioContext *aio_context;
BlockMirrorBackingMode backing_mode;
@@ -3479,21 +3379,14 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
int64_t size;
const char *format = arg->format;
- blk = blk_by_name(arg->device);
- if (!blk) {
- error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
- "Device '%s' not found", arg->device);
+ bs = qmp_get_root_bs(arg->device, errp);
+ if (!bs) {
return;
}
- aio_context = blk_get_aio_context(blk);
+ aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- if (!blk_is_available(blk)) {
- error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, arg->device);
- goto out;
- }
- bs = blk_bs(blk);
if (!arg->has_mode) {
arg->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
}
@@ -3630,21 +3523,13 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
Error **errp)
{
BlockDriverState *bs;
- BlockBackend *blk;
BlockDriverState *target_bs;
AioContext *aio_context;
BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN;
Error *local_err = NULL;
- blk = blk_by_name(device);
- if (!blk) {
- error_setg(errp, "Device '%s' not found", device);
- return;
- }
- bs = blk_bs(blk);
-
+ bs = qmp_get_root_bs(device, errp);
if (!bs) {
- error_setg(errp, "Device '%s' has no media", device);
return;
}
@@ -3785,7 +3670,6 @@ void qmp_change_backing_file(const char *device,
const char *backing_file,
Error **errp)
{
- BlockBackend *blk;
BlockDriverState *bs = NULL;
AioContext *aio_context;
BlockDriverState *image_bs = NULL;
@@ -3794,22 +3678,14 @@ void qmp_change_backing_file(const char *device,
int open_flags;
int ret;
- blk = blk_by_name(device);
- if (!blk) {
- error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
- "Device '%s' not found", device);
+ bs = qmp_get_root_bs(device, errp);
+ if (!bs) {
return;
}
- aio_context = blk_get_aio_context(blk);
+ aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- if (!blk_is_available(blk)) {
- error_setg(errp, "Device '%s' has no medium", device);
- goto out;
- }
- bs = blk_bs(blk);
-
image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err);
if (local_err) {
error_propagate(errp, local_err);
diff --git a/blockjob.c b/blockjob.c
index a5ba3bee52..a167f96fd4 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -132,6 +132,10 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
if (job_id == NULL) {
job_id = bdrv_get_device_name(bs);
+ if (!*job_id) {
+ error_setg(errp, "An explicit job ID is required for this node");
+ return NULL;
+ }
}
if (!id_wellformed(job_id)) {
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 848efee5d1..74f32e515c 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1182,8 +1182,8 @@ ETEXI
{
.name = "drive_backup",
- .args_type = "reuse:-n,full:-f,device:B,target:s,format:s?",
- .params = "[-n] [-f] device target [format]",
+ .args_type = "reuse:-n,full:-f,compress:-c,device:B,target:s,format:s?",
+ .params = "[-n] [-f] [-c] device target [format]",
.help = "initiates a point-in-time\n\t\t\t"
"copy for a device. The device's contents are\n\t\t\t"
"copied to the new image file, excluding data that\n\t\t\t"
@@ -1191,7 +1191,9 @@ ETEXI
"The -n flag requests QEMU to reuse the image found\n\t\t\t"
"in new-image-file, instead of recreating it from scratch.\n\t\t\t"
"The -f flag requests QEMU to copy the whole disk,\n\t\t\t"
- "so that the result does not need a backing file.\n\t\t\t",
+ "so that the result does not need a backing file.\n\t\t\t"
+ "The -c flag requests QEMU to compress backup data\n\t\t\t"
+ "(if the target format supports it).\n\t\t\t",
.mhandler.cmd = hmp_drive_backup,
},
STEXI
diff --git a/hmp.c b/hmp.c
index cc2056e9e2..ad33b442d2 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1109,8 +1109,19 @@ void hmp_drive_backup(Monitor *mon, const QDict *qdict)
const char *format = qdict_get_try_str(qdict, "format");
bool reuse = qdict_get_try_bool(qdict, "reuse", false);
bool full = qdict_get_try_bool(qdict, "full", false);
- enum NewImageMode mode;
+ bool compress = qdict_get_try_bool(qdict, "compress", false);
Error *err = NULL;
+ DriveBackup backup = {
+ .device = (char *)device,
+ .target = (char *)filename,
+ .has_format = !!format,
+ .format = (char *)format,
+ .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
+ .has_mode = true,
+ .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS,
+ .has_compress = !!compress,
+ .compress = compress,
+ };
if (!filename) {
error_setg(&err, QERR_MISSING_PARAMETER, "target");
@@ -1118,16 +1129,7 @@ void hmp_drive_backup(Monitor *mon, const QDict *qdict)
return;
}
- if (reuse) {
- mode = NEW_IMAGE_MODE_EXISTING;
- } else {
- mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
- }
-
- qmp_drive_backup(false, NULL, device, filename, !!format, format,
- full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
- true, mode, false, 0, false, NULL,
- false, 0, false, 0, &err);
+ qmp_drive_backup(&backup, &err);
hmp_handle_error(mon, &err);
}
@@ -1921,11 +1923,22 @@ void hmp_chardev_remove(Monitor *mon, const QDict *qdict)
void hmp_qemu_io(Monitor *mon, const QDict *qdict)
{
BlockBackend *blk;
+ BlockBackend *local_blk = NULL;
const char* device = qdict_get_str(qdict, "device");
const char* command = qdict_get_str(qdict, "command");
Error *err = NULL;
blk = blk_by_name(device);
+ if (!blk) {
+ BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err);
+ if (bs) {
+ blk = local_blk = blk_new();
+ blk_insert_bs(blk, bs);
+ } else {
+ goto fail;
+ }
+ }
+
if (blk) {
AioContext *aio_context = blk_get_aio_context(blk);
aio_context_acquire(aio_context);
@@ -1938,6 +1951,8 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
"Device '%s' not found", device);
}
+fail:
+ blk_unref(local_blk);
hmp_handle_error(mon, &err);
}
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index 67c76bfcd6..2eb055ae70 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -75,10 +75,6 @@ static int ide_qdev_init(DeviceState *qdev)
IDEDeviceClass *dc = IDE_DEVICE_GET_CLASS(dev);
IDEBus *bus = DO_UPCAST(IDEBus, qbus, qdev->parent_bus);
- if (!dev->conf.blk) {
- error_report("No drive specified");
- goto err;
- }
if (dev->unit == -1) {
dev->unit = bus->master ? 1 : 0;
}
@@ -158,6 +154,16 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
IDEState *s = bus->ifs + dev->unit;
Error *err = NULL;
+ if (!dev->conf.blk) {
+ if (kind != IDE_CD) {
+ error_report("No drive specified");
+ return -1;
+ } else {
+ /* Anonymous BlockBackend for an empty drive */
+ dev->conf.blk = blk_new();
+ }
+ }
+
if (dev->conf.discard_granularity == -1) {
dev->conf.discard_granularity = 512;
} else if (dev->conf.discard_granularity &&
@@ -257,7 +263,11 @@ static int ide_cd_initfn(IDEDevice *dev)
static int ide_drive_initfn(IDEDevice *dev)
{
- DriveInfo *dinfo = blk_legacy_dinfo(dev->conf.blk);
+ DriveInfo *dinfo = NULL;
+
+ if (dev->conf.blk) {
+ dinfo = blk_legacy_dinfo(dev->conf.blk);
+ }
return ide_dev_initfn(dev, dinfo && dinfo->media_cd ? IDE_CD : IDE_HD);
}
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 836a1553ed..99c9d618da 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -2359,6 +2359,11 @@ static void scsi_hd_realize(SCSIDevice *dev, Error **errp)
static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+
+ if (!dev->conf.blk) {
+ dev->conf.blk = blk_new();
+ }
+
s->qdev.blocksize = 2048;
s->qdev.type = TYPE_ROM;
s->features |= 1 << SCSI_DISK_F_REMOVABLE;
diff --git a/include/block/block.h b/include/block/block.h
index 11c162d594..7edce5c35f 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -65,9 +65,10 @@ typedef enum {
BDRV_REQ_MAY_UNMAP = 0x4,
BDRV_REQ_NO_SERIALISING = 0x8,
BDRV_REQ_FUA = 0x10,
+ BDRV_REQ_WRITE_COMPRESSED = 0x20,
/* Mask of valid flags */
- BDRV_REQ_MASK = 0x1f,
+ BDRV_REQ_MASK = 0x3f,
} BdrvRequestFlags;
typedef struct BlockSizes {
@@ -399,8 +400,6 @@ const char *bdrv_get_node_name(const BlockDriverState *bs);
const char *bdrv_get_device_name(const BlockDriverState *bs);
const char *bdrv_get_device_or_node_name(const BlockDriverState *bs);
int bdrv_get_flags(BlockDriverState *bs);
-int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
- const uint8_t *buf, int nb_sectors);
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs);
void bdrv_round_sectors_to_clusters(BlockDriverState *bs,
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 1e939de4fe..0ca6a78eb3 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -204,8 +204,8 @@ struct BlockDriver {
bool has_variable_length;
int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs);
- int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num,
- const uint8_t *buf, int nb_sectors);
+ int coroutine_fn (*bdrv_co_pwritev_compressed)(BlockDriverState *bs,
+ uint64_t offset, uint64_t bytes, QEMUIOVector *qiov);
int (*bdrv_snapshot_create)(BlockDriverState *bs,
QEMUSnapshotInfo *sn_info);
@@ -765,6 +765,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
void backup_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *target, int64_t speed,
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
+ bool compress,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
BlockCompletionFunc *cb, void *opaque,
diff --git a/include/block/nbd.h b/include/block/nbd.h
index 1897557a9b..80610ff31b 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -103,8 +103,9 @@ int nbd_disconnect(int fd);
typedef struct NBDExport NBDExport;
typedef struct NBDClient NBDClient;
-NBDExport *nbd_export_new(BlockBackend *blk, off_t dev_offset, off_t size,
+NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
uint16_t nbdflags, void (*close)(NBDExport *),
+ bool writethrough, BlockBackend *on_eject_blk,
Error **errp);
void nbd_export_close(NBDExport *exp);
void nbd_export_get(NBDExport *exp);
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
index ac8d4c9cc8..29a20782f0 100644
--- a/include/qemu/coroutine.h
+++ b/include/qemu/coroutine.h
@@ -143,6 +143,7 @@ bool qemu_co_queue_empty(CoQueue *queue);
*/
typedef struct CoMutex {
bool locked;
+ Coroutine *holder;
CoQueue queue;
} CoMutex;
diff --git a/include/qemu/coroutine_int.h b/include/qemu/coroutine_int.h
index 581a7f5140..6df9d33352 100644
--- a/include/qemu/coroutine_int.h
+++ b/include/qemu/coroutine_int.h
@@ -39,6 +39,7 @@ struct Coroutine {
void *entry_arg;
Coroutine *caller;
QSLIST_ENTRY(Coroutine) pool_next;
+ size_t locks_held;
/* Coroutines that should be woken up when we yield or terminate */
QSIMPLEQ_HEAD(, Coroutine) co_queue_wakeup;
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 2da4905d18..4808a9621a 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -98,6 +98,7 @@ BlockDriverState *blk_bs(BlockBackend *blk);
void blk_remove_bs(BlockBackend *blk);
void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs);
bool bdrv_has_blk(BlockDriverState *bs);
+bool bdrv_is_root_node(BlockDriverState *bs);
void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow);
void blk_iostatus_enable(BlockBackend *blk);
@@ -203,8 +204,8 @@ void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk,
BlockCompletionFunc *cb, void *opaque);
int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset,
int count, BdrvRequestFlags flags);
-int blk_write_compressed(BlockBackend *blk, int64_t sector_num,
- const uint8_t *buf, int nb_sectors);
+int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf,
+ int count);
int blk_truncate(BlockBackend *blk, int64_t offset);
int blk_pdiscard(BlockBackend *blk, int64_t offset, int count);
int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,
diff --git a/nbd/server.c b/nbd/server.c
index 80fbb4da1d..472f584c32 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -69,6 +69,7 @@ struct NBDExport {
AioContext *ctx;
+ BlockBackend *eject_notifier_blk;
Notifier eject_notifier;
};
@@ -807,11 +808,18 @@ static void nbd_eject_notifier(Notifier *n, void *data)
nbd_export_close(exp);
}
-NBDExport *nbd_export_new(BlockBackend *blk, off_t dev_offset, off_t size,
+NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
uint16_t nbdflags, void (*close)(NBDExport *),
+ bool writethrough, BlockBackend *on_eject_blk,
Error **errp)
{
+ BlockBackend *blk;
NBDExport *exp = g_malloc0(sizeof(NBDExport));
+
+ blk = blk_new();
+ blk_insert_bs(blk, bs);
+ blk_set_enable_write_cache(blk, !writethrough);
+
exp->refcount = 1;
QTAILQ_INIT(&exp->clients);
exp->blk = blk;
@@ -827,11 +835,14 @@ NBDExport *nbd_export_new(BlockBackend *blk, off_t dev_offset, off_t size,
exp->close = close;
exp->ctx = blk_get_aio_context(blk);
- blk_ref(blk);
blk_add_aio_context_notifier(blk, blk_aio_attached, blk_aio_detach, exp);
- exp->eject_notifier.notify = nbd_eject_notifier;
- blk_add_remove_bs_notifier(blk, &exp->eject_notifier);
+ if (on_eject_blk) {
+ blk_ref(on_eject_blk);
+ exp->eject_notifier_blk = on_eject_blk;
+ exp->eject_notifier.notify = nbd_eject_notifier;
+ blk_add_remove_bs_notifier(on_eject_blk, &exp->eject_notifier);
+ }
/*
* NBD exports are used for non-shared storage migration. Make sure
@@ -844,6 +855,7 @@ NBDExport *nbd_export_new(BlockBackend *blk, off_t dev_offset, off_t size,
return exp;
fail:
+ blk_unref(blk);
g_free(exp);
return NULL;
}
@@ -914,7 +926,10 @@ void nbd_export_put(NBDExport *exp)
}
if (exp->blk) {
- notifier_remove(&exp->eject_notifier);
+ if (exp->eject_notifier_blk) {
+ notifier_remove(&exp->eject_notifier);
+ blk_unref(exp->eject_notifier_blk);
+ }
blk_remove_aio_context_notifier(exp->blk, blk_aio_attached,
blk_aio_detach, exp);
blk_unref(exp->blk);
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 5e2d7d78d2..31f9990754 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -876,7 +876,7 @@
# @job-id: #optional identifier for the newly-created block job. If
# omitted, the device name will be used. (Since 2.7)
#
-# @device: the name of the device which should be copied.
+# @device: the device name or node-name of a root node which should be copied.
#
# @target: the target of the new image. If the file exists, or if it
# is a device, the existing file/device will be used as the new
@@ -898,6 +898,9 @@
# Must be present if sync is "incremental", must NOT be present
# otherwise. (Since 2.4)
#
+# @compress: #optional true to compress data, if the target format supports it.
+# (default: false) (since 2.7)
+#
# @on-source-error: #optional the action to take on an error on the source,
# default 'report'. 'stop' and 'enospc' can only be used
# if the block device supports io-status (see BlockInfo).
@@ -915,7 +918,7 @@
{ 'struct': 'DriveBackup',
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
'*format': 'str', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
- '*speed': 'int', '*bitmap': 'str',
+ '*speed': 'int', '*bitmap': 'str', '*compress': 'bool',
'*on-source-error': 'BlockdevOnError',
'*on-target-error': 'BlockdevOnError' } }
@@ -925,7 +928,7 @@
# @job-id: #optional identifier for the newly-created block job. If
# omitted, the device name will be used. (Since 2.7)
#
-# @device: the name of the device which should be copied.
+# @device: the device name or node-name of a root node which should be copied.
#
# @target: the device name or node-name of the backup target node.
#
@@ -936,6 +939,9 @@
# @speed: #optional the maximum speed, in bytes per second. The default is 0,
# for unlimited.
#
+# @compress: #optional true to compress data, if the target format supports it.
+# (default: false) (since 2.7)
+#
# @on-source-error: #optional the action to take on an error on the source,
# default 'report'. 'stop' and 'enospc' can only be used
# if the block device supports io-status (see BlockInfo).
@@ -954,6 +960,7 @@
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
'sync': 'MirrorSyncMode',
'*speed': 'int',
+ '*compress': 'bool',
'*on-source-error': 'BlockdevOnError',
'*on-target-error': 'BlockdevOnError' } }
@@ -998,7 +1005,8 @@
# @image-node-name: The name of the block driver state node of the
# image to modify.
#
-# @device: The name of the device that owns image-node-name.
+# @device: The device name or node-name of the root node that owns
+# image-node-name.
#
# @backing-file: The string to write as the backing file. This
# string is not validated, so care should be taken
@@ -1020,7 +1028,7 @@
# @job-id: #optional identifier for the newly-created block job. If
# omitted, the device name will be used. (Since 2.7)
#
-# @device: the name of the device
+# @device: the device name or node-name of a root node
#
# @base: #optional The file name of the backing image to write data into.
# If not specified, this is the deepest backing image
@@ -1086,11 +1094,12 @@
# For the arguments, see the documentation of DriveBackup.
#
# Returns: nothing on success
-# If @device is not a valid block device, DeviceNotFound
+# If @device is not a valid block device, GenericError
#
# Since 1.6
##
-{ 'command': 'drive-backup', 'data': 'DriveBackup' }
+{ 'command': 'drive-backup', 'boxed': true,
+ 'data': 'DriveBackup' }
##
# @blockdev-backup
@@ -1103,9 +1112,13 @@
#
# For the arguments, see the documentation of BlockdevBackup.
#
+# Returns: nothing on success
+# If @device is not a valid block device, DeviceNotFound
+#
# Since 2.3
##
-{ 'command': 'blockdev-backup', 'data': 'BlockdevBackup' }
+{ 'command': 'blockdev-backup', 'boxed': true,
+ 'data': 'BlockdevBackup' }
##
@@ -1127,7 +1140,7 @@
# See DriveMirror for parameter descriptions
#
# Returns: nothing on success
-# If @device is not a valid block device, DeviceNotFound
+# If @device is not a valid block device, GenericError
#
# Since 1.3
##
@@ -1142,7 +1155,8 @@
# @job-id: #optional identifier for the newly-created block job. If
# omitted, the device name will be used. (Since 2.7)
#
-# @device: the name of the device whose writes should be mirrored.
+# @device: the device name or node-name of a root node whose writes should be
+# mirrored.
#
# @target: the target of the new image. If the file exists, or if it
# is a device, the existing file/device will be used as the new
@@ -1277,7 +1291,8 @@
# @job-id: #optional identifier for the newly-created block job. If
# omitted, the device name will be used. (Since 2.7)
#
-# @device: the name of the device whose writes should be mirrored.
+# @device: The device name or node-name of a root node whose writes should be
+# mirrored.
#
# @target: the id or node-name of the block device to mirror to. This mustn't be
# attached to guest.
@@ -1462,7 +1477,7 @@
# @job-id: #optional identifier for the newly-created block job. If
# omitted, the device name will be used. (Since 2.7)
#
-# @device: the device name
+# @device: the device name or node-name of a root node
#
# @base: #optional the common backing file name
#
@@ -1487,9 +1502,6 @@
# 'stop' and 'enospc' can only be used if the block device
# supports io-status (see BlockInfo). Since 1.3.
#
-# Returns: Nothing on success
-# If @device does not exist, DeviceNotFound
-#
# Since: 1.1
##
{ 'command': 'block-stream',
diff --git a/qapi/block.json b/qapi/block.json
index 937337dce5..8b08bd2fd0 100644
--- a/qapi/block.json
+++ b/qapi/block.json
@@ -58,7 +58,8 @@
##
# @BlockdevSnapshotInternal
#
-# @device: the name of the device to generate the snapshot from
+# @device: the device name or node-name of a root node to generate the snapshot
+# from
#
# @name: the name of the internal snapshot to be created
#
@@ -80,7 +81,7 @@
# For the arguments, see the documentation of BlockdevSnapshotInternal.
#
# Returns: nothing on success
-# If @device is not a valid block device, DeviceNotFound
+# If @device is not a valid block device, GenericError
# If any snapshot matching @name exists, or @name is empty,
# GenericError
# If the format of the image used does not support it,
@@ -99,14 +100,15 @@
# both. One of the name or id is required. Return SnapshotInfo for the
# successfully deleted snapshot.
#
-# @device: the name of the device to delete the snapshot from
+# @device: the device name or node-name of a root node to delete the snapshot
+# from
#
# @id: optional the snapshot's ID to be deleted
#
# @name: optional the snapshot's name to be deleted
#
# Returns: SnapshotInfo on success
-# If @device is not a valid block device, DeviceNotFound
+# If @device is not a valid block device, GenericError
# If snapshot not found, GenericError
# If the format of the image used does not support it,
# BlockFormatFeatureNotSupported
@@ -159,9 +161,9 @@
##
# @nbd-server-add:
#
-# Export a device to QEMU's embedded NBD server.
+# Export a block node to QEMU's embedded NBD server.
#
-# @device: Block device to be exported
+# @device: The device name or node name of the node to be exported
#
# @writable: Whether clients should be able to write to the device via the
# NBD connection (default false). #optional
diff --git a/qemu-img.c b/qemu-img.c
index f204d04136..1090286a9f 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -1590,7 +1590,9 @@ static int convert_write(ImgConvertState *s, int64_t sector_num, int nb_sectors,
break;
}
- ret = blk_write_compressed(s->target, sector_num, buf, n);
+ ret = blk_pwrite_compressed(s->target,
+ sector_num << BDRV_SECTOR_BITS,
+ buf, n << BDRV_SECTOR_BITS);
if (ret < 0) {
return ret;
}
@@ -1727,7 +1729,7 @@ static int convert_do_copy(ImgConvertState *s)
if (s->compressed) {
/* signal EOF to align */
- ret = blk_write_compressed(s->target, 0, NULL, 0);
+ ret = blk_pwrite_compressed(s->target, 0, NULL, 0);
if (ret < 0) {
goto fail;
}
@@ -2032,7 +2034,7 @@ static int img_convert(int argc, char **argv)
const char *preallocation =
qemu_opt_get(opts, BLOCK_OPT_PREALLOC);
- if (!drv->bdrv_write_compressed) {
+ if (!drv->bdrv_co_pwritev_compressed) {
error_report("Compression not supported for this file format");
ret = -1;
goto out;
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index 25954f5634..3a3838a079 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -504,7 +504,7 @@ static int do_write_compressed(BlockBackend *blk, char *buf, int64_t offset,
return -ERANGE;
}
- ret = blk_write_compressed(blk, offset >> 9, (uint8_t *)buf, count >> 9);
+ ret = blk_pwrite_compressed(blk, offset, buf, count);
if (ret < 0) {
return ret;
}
diff --git a/qemu-nbd.c b/qemu-nbd.c
index e3571c2025..99297a556f 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -910,8 +910,8 @@ int main(int argc, char **argv)
}
}
- exp = nbd_export_new(blk, dev_offset, fd_size, nbdflags, nbd_export_closed,
- &local_err);
+ exp = nbd_export_new(bs, dev_offset, fd_size, nbdflags, nbd_export_closed,
+ writethrough, NULL, &local_err);
if (!exp) {
error_report_err(local_err);
exit(EXIT_FAILURE);
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 5c8d1d5e9f..e6c9193600 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -1120,7 +1120,7 @@ Arguments:
- "job-id": Identifier for the newly-created block job. If omitted,
the device name will be used. (json-string, optional)
-- "device": The device's ID, must be unique (json-string)
+- "device": The device name or node-name of a root node (json-string)
- "base": The file name of the backing image above which copying starts
(json-string, optional)
- "backing-file": The backing file string to write into the active layer. This
@@ -1166,7 +1166,7 @@ Arguments:
- "job-id": Identifier for the newly-created block job. If omitted,
the device name will be used. (json-string, optional)
-- "device": The device's ID, must be unique (json-string)
+- "device": The device name or node-name of a root node (json-string)
- "base": The file name of the backing image to write data into.
If not specified, this is the deepest backing image
(json-string, optional)
@@ -1217,7 +1217,8 @@ EQMP
{
.name = "drive-backup",
.args_type = "job-id:s?,sync:s,device:B,target:s,speed:i?,mode:s?,"
- "format:s?,bitmap:s?,on-source-error:s?,on-target-error:s?",
+ "format:s?,bitmap:s?,compress:b?,"
+ "on-source-error:s?,on-target-error:s?",
.mhandler.cmd_new = qmp_marshal_drive_backup,
},
@@ -1235,7 +1236,7 @@ Arguments:
- "job-id": Identifier for the newly-created block job. If omitted,
the device name will be used. (json-string, optional)
-- "device": the name of the device which should be copied.
+- "device": the device name or node-name of a root node which should be copied.
(json-string)
- "target": the target of the new image. If the file exists, or if it is a
device, the existing file/device will be used as the new
@@ -1253,6 +1254,8 @@ Arguments:
- "mode": whether and how QEMU should create a new image
(NewImageMode, optional, default 'absolute-paths')
- "speed": the maximum speed, in bytes per second (json-int, optional)
+- "compress": true to compress data, if the target format supports it.
+ (json-bool, optional, default false)
- "on-source-error": the action to take on an error on the source, default
'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status.
@@ -1272,7 +1275,7 @@ EQMP
{
.name = "blockdev-backup",
- .args_type = "job-id:s?,sync:s,device:B,target:B,speed:i?,"
+ .args_type = "job-id:s?,sync:s,device:B,target:B,speed:i?,compress:b?,"
"on-source-error:s?,on-target-error:s?",
.mhandler.cmd_new = qmp_marshal_blockdev_backup,
},
@@ -1288,7 +1291,7 @@ Arguments:
- "job-id": Identifier for the newly-created block job. If omitted,
the device name will be used. (json-string, optional)
-- "device": the name of the device which should be copied.
+- "device": the device name or node-name of a root node which should be copied.
(json-string)
- "target": the name of the backup target device. (json-string)
- "sync": what parts of the disk image should be copied to the destination;
@@ -1296,6 +1299,8 @@ Arguments:
sectors allocated in the topmost image, or "none" to only replicate
new I/O (MirrorSyncMode).
- "speed": the maximum speed, in bytes per second (json-int, optional)
+- "compress": true to compress data, if the target format supports it.
+ (json-bool, optional, default false)
- "on-source-error": the action to take on an error on the source, default
'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status.
@@ -1407,7 +1412,8 @@ actions array:
- "mode": whether and how QEMU should create the snapshot file
(NewImageMode, optional, default "absolute-paths")
When "type" is "blockdev-snapshot-internal-sync":
- - "device": device name to snapshot (json-string)
+ - "device": the device name or node-name of a root node to snapshot
+ (json-string)
- "name": name of the new snapshot (json-string)
Example:
@@ -1608,7 +1614,8 @@ name already exists, the operation will fail.
Arguments:
-- "device": device name to snapshot (json-string)
+- "device": the device name or node-name of a root node to snapshot
+ (json-string)
- "name": name of the new snapshot (json-string)
Example:
@@ -1639,7 +1646,7 @@ fail.
Arguments:
-- "device": device name (json-string)
+- "device": the device name or node-name of a root node (json-string)
- "id": ID of the snapshot (json-string, optional)
- "name": name of the snapshot (json-string, optional)
@@ -1687,7 +1694,8 @@ Arguments:
- "job-id": Identifier for the newly-created block job. If omitted,
the device name will be used. (json-string, optional)
-- "device": device name to operate on (json-string)
+- "device": the device name or node-name of a root node whose writes should be
+ mirrored. (json-string)
- "target": name of new image file (json-string)
- "format": format of new image (json-string, optional)
- "node-name": the name of the new block driver state in the node graph
@@ -1747,7 +1755,8 @@ Arguments:
- "job-id": Identifier for the newly-created block job. If omitted,
the device name will be used. (json-string, optional)
-- "device": device name to operate on (json-string)
+- "device": The device name or node-name of a root node whose writes should be
+ mirrored (json-string)
- "target": device name to mirror to (json-string)
- "replaces": the block driver node name to replace when finished
(json-string, optional)
@@ -1803,7 +1812,8 @@ Arguments:
"device".
(json-string, optional)
-- "device": The name of the device.
+- "device": The device name or node-name of the root node that owns
+ image-node-name.
(json-string)
- "backing-file": The string to write as the backing file. This string is
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
index 3ac2443e5b..107049b50f 100755
--- a/tests/qemu-iotests/030
+++ b/tests/qemu-iotests/030
@@ -126,7 +126,7 @@ class TestSingleDrive(iotests.QMPTestCase):
def test_device_not_found(self):
result = self.vm.qmp('block-stream', device='nonexistent')
- self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+ self.assert_qmp(result, 'error/class', 'GenericError')
class TestSmallerBackingFile(iotests.QMPTestCase):
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
index cbf5e0ba5c..80939c0d0d 100755
--- a/tests/qemu-iotests/041
+++ b/tests/qemu-iotests/041
@@ -38,7 +38,6 @@ class TestSingleDrive(iotests.QMPTestCase):
image_len = 1 * 1024 * 1024 # MB
qmp_cmd = 'drive-mirror'
qmp_target = target_img
- not_found_error = 'DeviceNotFound'
def setUp(self):
iotests.create_image(backing_img, self.image_len)
@@ -176,7 +175,7 @@ class TestSingleDrive(iotests.QMPTestCase):
result = self.vm.qmp(self.qmp_cmd, device='ide1-cd0', sync='full',
target=self.qmp_target)
- self.assert_qmp(result, 'error/class', self.not_found_error)
+ self.assert_qmp(result, 'error/class', 'GenericError')
def test_image_not_found(self):
result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
@@ -186,12 +185,11 @@ class TestSingleDrive(iotests.QMPTestCase):
def test_device_not_found(self):
result = self.vm.qmp(self.qmp_cmd, device='nonexistent', sync='full',
target=self.qmp_target)
- self.assert_qmp(result, 'error/class', self.not_found_error)
+ self.assert_qmp(result, 'error/class', 'GenericError')
class TestSingleBlockdev(TestSingleDrive):
qmp_cmd = 'blockdev-mirror'
qmp_target = 'node1'
- not_found_error = 'GenericError'
def setUp(self):
TestSingleDrive.setUp(self)
@@ -922,7 +920,7 @@ class TestRepairQuorum(iotests.QMPTestCase):
node_name='repair0',
replaces='img1',
target=quorum_repair_img, format=iotests.imgfmt)
- self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+ self.assert_qmp(result, 'error/class', 'GenericError')
def test_wrong_sync_mode(self):
if not self.has_quorum():
diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055
index c8e3578702..ff4535e3ea 100755
--- a/tests/qemu-iotests/055
+++ b/tests/qemu-iotests/055
@@ -134,10 +134,7 @@ class TestSingleDrive(iotests.QMPTestCase):
def do_test_device_not_found(self, cmd, **args):
result = self.vm.qmp(cmd, **args)
- if cmd == 'drive-backup':
- self.assert_qmp(result, 'error/class', 'DeviceNotFound')
- else:
- self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_qmp(result, 'error/class', 'GenericError')
def test_device_not_found(self):
self.do_test_device_not_found('drive-backup', device='nonexistent',
@@ -371,7 +368,7 @@ class TestSingleTransaction(iotests.QMPTestCase):
'sync': 'full' },
}
])
- self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+ self.assert_qmp(result, 'error/class', 'GenericError')
result = self.vm.qmp('transaction', actions=[{
'type': 'blockdev-backup',
@@ -451,5 +448,123 @@ class TestSingleTransaction(iotests.QMPTestCase):
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_no_active_block_jobs()
+
+class TestDriveCompression(iotests.QMPTestCase):
+ image_len = 64 * 1024 * 1024 # MB
+ fmt_supports_compression = [{'type': 'qcow2', 'args': ()},
+ {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')}]
+
+ def setUp(self):
+ # Write data to the image so we can compare later
+ qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestDriveCompression.image_len))
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x11 0 64k', test_img)
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x00 64k 128k', test_img)
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x22 162k 32k', test_img)
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x33 67043328 64k', test_img)
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(test_img)
+ os.remove(blockdev_target_img)
+ try:
+ os.remove(target_img)
+ except OSError:
+ pass
+
+ def do_prepare_drives(self, fmt, args):
+ self.vm = iotests.VM().add_drive(test_img)
+
+ qemu_img('create', '-f', fmt, blockdev_target_img,
+ str(TestDriveCompression.image_len), *args)
+ self.vm.add_drive(blockdev_target_img, format=fmt)
+
+ self.vm.launch()
+
+ def do_test_compress_complete(self, cmd, format, **args):
+ self.do_prepare_drives(format['type'], format['args'])
+
+ self.assert_no_active_block_jobs()
+
+ result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_until_completed()
+
+ self.vm.shutdown()
+ self.assertTrue(iotests.compare_images(test_img, blockdev_target_img,
+ iotests.imgfmt, format['type']),
+ 'target image does not match source after backup')
+
+ def test_complete_compress_drive_backup(self):
+ for format in TestDriveCompression.fmt_supports_compression:
+ self.do_test_compress_complete('drive-backup', format,
+ target=blockdev_target_img, mode='existing')
+
+ def test_complete_compress_blockdev_backup(self):
+ for format in TestDriveCompression.fmt_supports_compression:
+ self.do_test_compress_complete('blockdev-backup', format, target='drive1')
+
+ def do_test_compress_cancel(self, cmd, format, **args):
+ self.do_prepare_drives(format['type'], format['args'])
+
+ self.assert_no_active_block_jobs()
+
+ result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
+ self.assert_qmp(result, 'return', {})
+
+ event = self.cancel_and_wait()
+ self.assert_qmp(event, 'data/type', 'backup')
+
+ self.vm.shutdown()
+
+ def test_compress_cancel_drive_backup(self):
+ for format in TestDriveCompression.fmt_supports_compression:
+ self.do_test_compress_cancel('drive-backup', format,
+ target=blockdev_target_img, mode='existing')
+
+ def test_compress_cancel_blockdev_backup(self):
+ for format in TestDriveCompression.fmt_supports_compression:
+ self.do_test_compress_cancel('blockdev-backup', format, target='drive1')
+
+ def do_test_compress_pause(self, cmd, format, **args):
+ self.do_prepare_drives(format['type'], format['args'])
+
+ self.assert_no_active_block_jobs()
+
+ self.vm.pause_drive('drive0')
+ result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('block-job-pause', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ self.vm.resume_drive('drive0')
+ time.sleep(1)
+ result = self.vm.qmp('query-block-jobs')
+ offset = self.dictpath(result, 'return[0]/offset')
+
+ time.sleep(1)
+ result = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(result, 'return[0]/offset', offset)
+
+ result = self.vm.qmp('block-job-resume', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_until_completed()
+
+ self.vm.shutdown()
+ self.assertTrue(iotests.compare_images(test_img, blockdev_target_img,
+ iotests.imgfmt, format['type']),
+ 'target image does not match source after backup')
+
+ def test_compress_pause_drive_backup(self):
+ for format in TestDriveCompression.fmt_supports_compression:
+ self.do_test_compress_pause('drive-backup', format,
+ target=blockdev_target_img, mode='existing')
+
+ def test_compress_pause_blockdev_backup(self):
+ for format in TestDriveCompression.fmt_supports_compression:
+ self.do_test_compress_pause('blockdev-backup', format, target='drive1')
+
if __name__ == '__main__':
iotests.main(supported_fmts=['raw', 'qcow2'])
diff --git a/tests/qemu-iotests/055.out b/tests/qemu-iotests/055.out
index 42314e9c00..5ce2f9a2ed 100644
--- a/tests/qemu-iotests/055.out
+++ b/tests/qemu-iotests/055.out
@@ -1,5 +1,5 @@
-........................
+..............................
----------------------------------------------------------------------
-Ran 24 tests
+Ran 30 tests
OK
diff --git a/tests/qemu-iotests/057 b/tests/qemu-iotests/057
index 9cdd582e39..9f0a5a3057 100755
--- a/tests/qemu-iotests/057
+++ b/tests/qemu-iotests/057
@@ -182,7 +182,7 @@ class TestSingleTransaction(ImageSnapshotTestCase):
'name': 'a' },
}]
result = self.vm.qmp('transaction', actions = actions)
- self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+ self.assert_qmp(result, 'error/class', 'GenericError')
def test_error_exist(self):
self.createSnapshotInTransaction(1)
@@ -239,7 +239,7 @@ class TestSnapshotDelete(ImageSnapshotTestCase):
result = self.vm.qmp('blockdev-snapshot-delete-internal-sync',
device = 'drive_error',
id = '0')
- self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+ self.assert_qmp(result, 'error/class', 'GenericError')
def test_error_no_id_and_name(self):
result = self.vm.qmp('blockdev-snapshot-delete-internal-sync',
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index dbe0ee548a..f1f36d7fc7 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -50,6 +50,7 @@ cachemode = os.environ.get('CACHEMODE')
qemu_default_machine = os.environ.get('QEMU_DEFAULT_MACHINE')
socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper')
+debug = False
def qemu_img(*args):
'''Run qemu-img and return the exit code'''
@@ -86,10 +87,10 @@ def qemu_io(*args):
sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args)))
return subp.communicate()[0]
-def compare_images(img1, img2):
+def compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt):
'''Return True if two image files are identical'''
- return qemu_img('compare', '-f', imgfmt,
- '-F', imgfmt, img1, img2) == 0
+ return qemu_img('compare', '-f', fmt1,
+ '-F', fmt2, img1, img2) == 0
def create_image(name, size):
'''Create a fully-allocated raw image with sector markers'''
@@ -134,6 +135,8 @@ class VM(qtest.QEMUQtestMachine):
def __init__(self):
super(VM, self).__init__(qemu_prog, qemu_opts, test_dir=test_dir,
socket_scm_helper=socket_scm_helper)
+ if debug:
+ self._debug = True
self._num_drives = 0
def add_drive_raw(self, opts):
@@ -141,14 +144,14 @@ class VM(qtest.QEMUQtestMachine):
self._args.append(opts)
return self
- def add_drive(self, path, opts='', interface='virtio'):
+ def add_drive(self, path, opts='', interface='virtio', format=imgfmt):
'''Add a virtio-blk drive to the VM'''
options = ['if=%s' % interface,
'id=drive%d' % self._num_drives]
if path is not None:
options.append('file=%s' % path)
- options.append('format=%s' % imgfmt)
+ options.append('format=%s' % format)
options.append('cache=%s' % cachemode)
if opts:
@@ -318,6 +321,8 @@ def verify_quorum():
def main(supported_fmts=[], supported_oses=['linux']):
'''Run tests'''
+ global debug
+
# We are using TEST_DIR and QEMU_DEFAULT_MACHINE as proxies to
# indicate that we're not being run via "check". There may be
# other things set up by "check" that individual test cases rely
diff --git a/tests/test-coroutine.c b/tests/test-coroutine.c
index ee5e06d327..6431dd6d7c 100644
--- a/tests/test-coroutine.c
+++ b/tests/test-coroutine.c
@@ -139,13 +139,20 @@ static void test_co_queue(void)
{
Coroutine *c1;
Coroutine *c2;
+ Coroutine tmp;
c2 = qemu_coroutine_create(c2_fn, NULL);
c1 = qemu_coroutine_create(c1_fn, c2);
qemu_coroutine_enter(c1);
+
+ /* c1 shouldn't be used any more now; make sure we segfault if it is */
+ tmp = *c1;
memset(c1, 0xff, sizeof(Coroutine));
qemu_coroutine_enter(c2);
+
+ /* Must restore the coroutine now to avoid corrupted pool */
+ *c1 = tmp;
}
/*
diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c
index 22aa9abb30..14cf9ce458 100644
--- a/util/qemu-coroutine-lock.c
+++ b/util/qemu-coroutine-lock.c
@@ -129,6 +129,8 @@ void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex)
}
mutex->locked = true;
+ mutex->holder = self;
+ self->locks_held++;
trace_qemu_co_mutex_lock_return(mutex, self);
}
@@ -140,9 +142,12 @@ void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex)
trace_qemu_co_mutex_unlock_entry(mutex, self);
assert(mutex->locked == true);
+ assert(mutex->holder == self);
assert(qemu_in_coroutine());
mutex->locked = false;
+ mutex->holder = NULL;
+ self->locks_held--;
qemu_co_queue_next(&mutex->queue);
trace_qemu_co_mutex_unlock_return(mutex, self);
@@ -156,14 +161,19 @@ void qemu_co_rwlock_init(CoRwlock *lock)
void qemu_co_rwlock_rdlock(CoRwlock *lock)
{
+ Coroutine *self = qemu_coroutine_self();
+
while (lock->writer) {
qemu_co_queue_wait(&lock->queue);
}
lock->reader++;
+ self->locks_held++;
}
void qemu_co_rwlock_unlock(CoRwlock *lock)
{
+ Coroutine *self = qemu_coroutine_self();
+
assert(qemu_in_coroutine());
if (lock->writer) {
lock->writer = false;
@@ -176,12 +186,16 @@ void qemu_co_rwlock_unlock(CoRwlock *lock)
qemu_co_queue_next(&lock->queue);
}
}
+ self->locks_held--;
}
void qemu_co_rwlock_wrlock(CoRwlock *lock)
{
+ Coroutine *self = qemu_coroutine_self();
+
while (lock->writer || lock->reader) {
qemu_co_queue_wait(&lock->queue);
}
lock->writer = true;
+ self->locks_held++;
}
diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c
index 89f21a9cec..3cbf225487 100644
--- a/util/qemu-coroutine.c
+++ b/util/qemu-coroutine.c
@@ -122,6 +122,7 @@ void qemu_coroutine_enter(Coroutine *co)
case COROUTINE_YIELD:
return;
case COROUTINE_TERMINATE:
+ assert(!co->locks_held);
trace_qemu_coroutine_terminate(co);
coroutine_delete(co);
return;