Block layer patches

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJXzpyJAAoJEH8JsnLIjy/WwToQAJ29bQ8dbVxybQtApZn0l3DH
 aBcguj822Vqa+KaxOLAfzkxmG5MurIvWzRQD1BvjxaprRykB+hDh4oAJCmVjfedP
 B28h24TUF+w8WIbpxf9weQFNpsT2Ire8ZySc0JZhpYMqxXCqy6NzDs98sjedDC0O
 jNbfic1L+yEpZumVE0Fzr4/YgPumt7wP0X42nb6G8R+VlChm3nweNCFF7hNQvTuB
 GNNbd9ckUS0BTcQazm04yRR/WzXW6uFqa00QeWsNGGd1mmZ0kUxiqxVgx/fuBMrL
 yC4LxFit7eNRoeVqu/nu8GsG+2Ol5zsalfJKFcoWmpg8pygOayc5SXecRUZRw7tg
 3oB7ZijbrBUFlr4y6cNVCGPtRluQshpLGHlgo68ulEIlHprqECwgPIdoOPr0bs+v
 Gb8ho2Y+lrISPIsjYWK5UFSmZf0SIBGILZUSD3lzQ+oOHXGKbdPAaFvSUqXENHSN
 xjtMYjr5t+NjrNNd2Q+VUJPlimHGw5jAowjsQSTk3ndcvJYeIVs+AwLqNTKc3dY4
 Oxx1IZ2RngDC63PmZUgh2Bs8pwFg7HaZJejmtq5jY8eHJZM/QMkCJX9TSRCZsMRB
 n0GxfCYabX526h3Yo94d74s5xRnHaC+Lem8PU/VEGR3/dMn21jf/PI9e+/0BnBTC
 iET2gkU70c4lunWkFHMd
 =LZpU
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Peter Maydell 2016-09-06 17:18:17 +01:00
commit 2926375cff
36 changed files with 589 additions and 618 deletions

View File

@ -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 */

View File

@ -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

View File

@ -409,6 +409,22 @@ bool bdrv_has_blk(BlockDriverState *bs)
return bdrv_first_blk(bs) != NULL;
}
/*
* 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.
*/
@ -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)

View File

@ -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;

View File

@ -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,

View File

@ -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);
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;
ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len);
if (ret < 0) {
goto fail;
}
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len);
ret = qcow2_co_pwritev(bs, offset, bytes, qiov, 0);
if (ret < 0) {
goto fail;
}
goto success;
}
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;
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,

View File

@ -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)
static int coroutine_fn
vmdk_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov)
{
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)
{
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,

View File

@ -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;
}

View File

@ -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);
bdrv_drained_begin(bs);
state->bs = bs;
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);
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 (!has_mode) {
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
if (!backup->has_mode) {
backup->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);

View File

@ -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)) {

View File

@ -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

37
hmp.c
View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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);

View File

@ -143,6 +143,7 @@ bool qemu_co_queue_empty(CoQueue *queue);
*/
typedef struct CoMutex {
bool locked;
Coroutine *holder;
CoQueue queue;
} CoMutex;

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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',

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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):

View File

@ -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():

View File

@ -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'])

View File

@ -1,5 +1,5 @@
........................
..............................
----------------------------------------------------------------------
Ran 24 tests
Ran 30 tests
OK

View File

@ -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',

View File

@ -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

View File

@ -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;
}
/*

View File

@ -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++;
}

View File

@ -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;