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 "trace.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/blockjob.h" #include "block/blockjob.h"
#include "block/nbd.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
@ -2206,6 +2207,7 @@ static void bdrv_close(BlockDriverState *bs)
void bdrv_close_all(void) void bdrv_close_all(void)
{ {
block_job_cancel_sync_all(); block_job_cancel_sync_all();
nbd_export_close_all();
/* Drop references from requests still in flight, such as canceled block /* Drop references from requests still in flight, such as canceled block
* jobs whose AIO context has not been polled yet */ * jobs whose AIO context has not been polled yet */

View File

@ -47,6 +47,7 @@ typedef struct BackupBlockJob {
uint64_t sectors_read; uint64_t sectors_read;
unsigned long *done_bitmap; unsigned long *done_bitmap;
int64_t cluster_size; int64_t cluster_size;
bool compress;
NotifierWithReturn before_write; NotifierWithReturn before_write;
QLIST_HEAD(, CowRequest) inflight_reqs; QLIST_HEAD(, CowRequest) inflight_reqs;
} BackupBlockJob; } BackupBlockJob;
@ -154,7 +155,8 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
bounce_qiov.size, BDRV_REQ_MAY_UNMAP); bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
} else { } else {
ret = blk_co_pwritev(job->target, start * job->cluster_size, 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) { if (ret < 0) {
trace_backup_do_cow_write_fail(job, start, ret); 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, void backup_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *target, int64_t speed, BlockDriverState *target, int64_t speed,
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap, MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
bool compress,
BlockdevOnError on_source_error, BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
BlockCompletionFunc *cb, void *opaque, BlockCompletionFunc *cb, void *opaque,
@ -507,6 +510,12 @@ void backup_start(const char *job_id, BlockDriverState *bs,
return; 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)) { if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
return; return;
} }
@ -555,6 +564,7 @@ void backup_start(const char *job_id, BlockDriverState *bs,
job->sync_mode = sync_mode; job->sync_mode = sync_mode;
job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ? job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ?
sync_bitmap : NULL; sync_bitmap : NULL;
job->compress = compress;
/* If there is no backing file on the target, we cannot rely on COW if our /* 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 * 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; 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. * 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; 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, int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
unsigned int bytes, QEMUIOVector *qiov, unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags) BdrvRequestFlags flags)
@ -1484,15 +1485,11 @@ int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset,
flags | BDRV_REQ_ZERO_WRITE); flags | BDRV_REQ_ZERO_WRITE);
} }
int blk_write_compressed(BlockBackend *blk, int64_t sector_num, int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf,
const uint8_t *buf, int nb_sectors) int count)
{ {
int ret = blk_check_request(blk, sector_num, nb_sectors); return blk_prw(blk, offset, (void *) buf, count, blk_write_entry,
if (ret < 0) { BDRV_REQ_WRITE_COMPRESSED);
return ret;
}
return bdrv_write_compressed(blk_bs(blk), sector_num, buf, nb_sectors);
} }
int blk_truncate(BlockBackend *blk, int64_t offset) 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; 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 { typedef struct RwCo {
BdrvChild *child; BdrvChild *child;
int64_t offset; int64_t offset;
@ -897,6 +886,19 @@ emulate_flags:
return ret; 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, static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs,
int64_t offset, unsigned int bytes, QEMUIOVector *qiov) 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) { } else if (flags & BDRV_REQ_ZERO_WRITE) {
bdrv_debug_event(bs, BLKDBG_PWRITEV_ZERO); bdrv_debug_event(bs, BLKDBG_PWRITEV_ZERO);
ret = bdrv_co_do_pwrite_zeroes(bs, offset, bytes, flags); 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) { } else if (bytes <= max_transfer) {
bdrv_debug_event(bs, BLKDBG_PWRITEV); bdrv_debug_event(bs, BLKDBG_PWRITEV);
ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, flags); ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, flags);
@ -1879,28 +1883,6 @@ int bdrv_is_allocated_above(BlockDriverState *top,
return 0; 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 { typedef struct BdrvVmstateCo {
BlockDriverState *bs; BlockDriverState *bs;
QEMUIOVector *qiov; QEMUIOVector *qiov;

View File

@ -913,75 +913,32 @@ static int qcow_make_empty(BlockDriverState *bs)
return 0; 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 /* XXX: put compressed sectors first, then all the cluster aligned
tables to avoid losing bytes in alignment */ tables to avoid losing bytes in alignment */
static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, static coroutine_fn int
const uint8_t *buf, int nb_sectors) qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
QEMUIOVector hd_qiov;
struct iovec iov;
z_stream strm; z_stream strm;
int ret, out_len; int ret, out_len;
uint8_t *out_buf; uint8_t *buf, *out_buf;
uint64_t cluster_offset; uint64_t cluster_offset;
if (nb_sectors != s->cluster_sectors) { buf = qemu_blockalign(bs, s->cluster_size);
ret = -EINVAL; if (bytes != s->cluster_size) {
if (bytes > s->cluster_size ||
/* Zero-pad last write if image size is not cluster aligned */ offset + bytes != bs->total_sectors << BDRV_SECTOR_BITS)
if (sector_num + nb_sectors == bs->total_sectors && {
nb_sectors < s->cluster_sectors) { qemu_vfree(buf);
uint8_t *pad_buf = qemu_blockalign(bs, s->cluster_size); return -EINVAL;
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);
} }
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); 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) { if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
/* could not compress: write normal cluster */ /* could not compress: write normal cluster */
ret = qcow_write(bs, sector_num, buf, s->cluster_sectors); ret = qcow_co_writev(bs, offset >> BDRV_SECTOR_BITS,
if (ret < 0) { bytes >> BDRV_SECTOR_BITS, qiov);
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);
if (ret < 0) { if (ret < 0) {
goto fail; 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; ret = 0;
fail: fail:
qemu_vfree(buf);
g_free(out_buf); g_free(out_buf);
return ret; return ret;
} }
@ -1085,7 +1050,7 @@ static BlockDriver bdrv_qcow = {
.bdrv_set_key = qcow_set_key, .bdrv_set_key = qcow_set_key,
.bdrv_make_empty = qcow_make_empty, .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, .bdrv_get_info = qcow_get_info,
.create_opts = &qcow_create_opts, .create_opts = &qcow_create_opts,

View File

@ -2533,84 +2533,39 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset)
return 0; 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 /* XXX: put compressed sectors first, then all the cluster aligned
tables to avoid losing bytes in alignment */ tables to avoid losing bytes in alignment */
static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num, static coroutine_fn int
const uint8_t *buf, int nb_sectors) qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
QEMUIOVector hd_qiov;
struct iovec iov;
z_stream strm; z_stream strm;
int ret, out_len; int ret, out_len;
uint8_t *out_buf; uint8_t *buf, *out_buf;
uint64_t cluster_offset; uint64_t cluster_offset;
if (nb_sectors == 0) { if (bytes == 0) {
/* align end of file to a sector boundary to ease reading with /* align end of file to a sector boundary to ease reading with
sector based I/Os */ sector based I/Os */
cluster_offset = bdrv_getlength(bs->file->bs); cluster_offset = bdrv_getlength(bs->file->bs);
return bdrv_truncate(bs->file->bs, cluster_offset); return bdrv_truncate(bs->file->bs, cluster_offset);
} }
if (nb_sectors != s->cluster_sectors) { buf = qemu_blockalign(bs, s->cluster_size);
ret = -EINVAL; if (bytes != s->cluster_size) {
if (bytes > s->cluster_size ||
/* Zero-pad last write if image size is not cluster aligned */ offset + bytes != bs->total_sectors << BDRV_SECTOR_BITS)
if (sector_num + nb_sectors == bs->total_sectors && {
nb_sectors < s->cluster_sectors) { qemu_vfree(buf);
uint8_t *pad_buf = qemu_blockalign(bs, s->cluster_size); return -EINVAL;
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);
} }
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); 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) { if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
/* could not compress: write normal cluster */ /* 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;
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);
if (ret < 0) { if (ret < 0) {
goto fail; 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; ret = 0;
fail: fail:
qemu_vfree(buf);
g_free(out_buf); g_free(out_buf);
return ret; return ret;
} }
@ -3412,7 +3378,7 @@ BlockDriver bdrv_qcow2 = {
.bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes, .bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes,
.bdrv_co_pdiscard = qcow2_co_pdiscard, .bdrv_co_pdiscard = qcow2_co_pdiscard,
.bdrv_truncate = qcow2_truncate, .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_make_empty = qcow2_make_empty,
.bdrv_snapshot_create = qcow2_snapshot_create, .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; return ret;
} }
typedef struct VmdkWriteCompressedCo { static int coroutine_fn
BlockDriverState *bs; vmdk_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
int64_t sector_num; uint64_t bytes, QEMUIOVector *qiov)
const uint8_t *buf;
int nb_sectors;
int ret;
} VmdkWriteCompressedCo;
static void vmdk_co_write_compressed(void *opaque)
{ {
VmdkWriteCompressedCo *co = opaque; return vmdk_co_pwritev(bs, offset, bytes, qiov, 0);
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;
}
} }
static int coroutine_fn vmdk_co_pwrite_zeroes(BlockDriverState *bs, 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_reopen_prepare = vmdk_reopen_prepare,
.bdrv_co_preadv = vmdk_co_preadv, .bdrv_co_preadv = vmdk_co_preadv,
.bdrv_co_pwritev = vmdk_co_pwritev, .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_co_pwrite_zeroes = vmdk_co_pwrite_zeroes,
.bdrv_close = vmdk_close, .bdrv_close = vmdk_close,
.bdrv_create = vmdk_create, .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, void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
Error **errp) Error **errp)
{ {
BlockBackend *blk; BlockDriverState *bs = NULL;
BlockBackend *on_eject_blk;
NBDExport *exp; NBDExport *exp;
if (!nbd_server) { if (!nbd_server) {
@ -158,26 +159,22 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
return; return;
} }
blk = blk_by_name(device); on_eject_blk = blk_by_name(device);
if (!blk) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, bs = bdrv_lookup_bs(device, device, errp);
"Device '%s' not found", device); if (!bs) {
return;
}
if (!blk_is_inserted(blk)) {
error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
return; return;
} }
if (!has_writable) { if (!has_writable) {
writable = false; writable = false;
} }
if (blk_is_read_only(blk)) { if (bdrv_is_read_only(bs)) {
writable = false; writable = false;
} }
exp = nbd_export_new(blk, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, NULL, exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY,
errp); NULL, false, on_eject_blk, errp);
if (!exp) { if (!exp) {
return; return;
} }

View File

@ -1174,6 +1174,28 @@ fail:
return dinfo; 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) void hmp_commit(Monitor *mon, const QDict *qdict)
{ {
const char *device = qdict_get_str(qdict, "device"); const char *device = qdict_get_str(qdict, "device");
@ -1284,21 +1306,17 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
Error **errp) Error **errp)
{ {
BlockDriverState *bs; BlockDriverState *bs;
BlockBackend *blk;
AioContext *aio_context; AioContext *aio_context;
QEMUSnapshotInfo sn; QEMUSnapshotInfo sn;
Error *local_err = NULL; Error *local_err = NULL;
SnapshotInfo *info = NULL; SnapshotInfo *info = NULL;
int ret; int ret;
blk = blk_by_name(device); bs = qmp_get_root_bs(device, errp);
if (!blk) { if (!bs) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", device);
return NULL; return NULL;
} }
aio_context = bdrv_get_aio_context(bs);
aio_context = blk_get_aio_context(blk);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
if (!has_id) { if (!has_id) {
@ -1314,12 +1332,6 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
goto out_aio_context; 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)) { if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) {
goto out_aio_context; goto out_aio_context;
} }
@ -1499,7 +1511,6 @@ static void internal_snapshot_prepare(BlkActionState *common,
Error *local_err = NULL; Error *local_err = NULL;
const char *device; const char *device;
const char *name; const char *name;
BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
QEMUSnapshotInfo old_sn, *sn; QEMUSnapshotInfo old_sn, *sn;
bool ret; bool ret;
@ -1522,23 +1533,15 @@ static void internal_snapshot_prepare(BlkActionState *common,
return; return;
} }
blk = blk_by_name(device); bs = qmp_get_root_bs(device, errp);
if (!blk) { if (!bs) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", device);
return; return;
} }
/* AioContext is released in .clean() */ /* 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); 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; state->bs = bs;
bdrv_drained_begin(bs); bdrv_drained_begin(bs);
@ -1838,56 +1841,31 @@ typedef struct DriveBackupState {
BlockJob *job; BlockJob *job;
} DriveBackupState; } DriveBackupState;
static void do_drive_backup(const char *job_id, const char *device, static void do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
const char *target, bool has_format, Error **errp);
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 drive_backup_prepare(BlkActionState *common, Error **errp) static void drive_backup_prepare(BlkActionState *common, Error **errp)
{ {
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
BlockBackend *blk; BlockDriverState *bs;
DriveBackup *backup; DriveBackup *backup;
Error *local_err = NULL; Error *local_err = NULL;
assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP); assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
backup = common->action->u.drive_backup.data; backup = common->action->u.drive_backup.data;
blk = blk_by_name(backup->device); bs = qmp_get_root_bs(backup->device, errp);
if (!blk) { if (!bs) {
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);
return; return;
} }
/* AioContext is released in .clean() */ /* 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); aio_context_acquire(state->aio_context);
bdrv_drained_begin(blk_bs(blk)); bdrv_drained_begin(bs);
state->bs = blk_bs(blk); state->bs = bs;
do_drive_backup(backup->has_job_id ? backup->job_id : NULL, do_drive_backup(backup, common->block_job_txn, &local_err);
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);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; return;
@ -1924,34 +1902,21 @@ typedef struct BlockdevBackupState {
AioContext *aio_context; AioContext *aio_context;
} BlockdevBackupState; } BlockdevBackupState;
static void do_blockdev_backup(const char *job_id, const char *device, static void do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
const char *target, enum MirrorSyncMode sync, Error **errp);
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 blockdev_backup_prepare(BlkActionState *common, Error **errp) static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
{ {
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
BlockdevBackup *backup; BlockdevBackup *backup;
BlockBackend *blk; BlockDriverState *bs, *target;
BlockDriverState *target;
Error *local_err = NULL; Error *local_err = NULL;
assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
backup = common->action->u.blockdev_backup.data; backup = common->action->u.blockdev_backup.data;
blk = blk_by_name(backup->device); bs = qmp_get_root_bs(backup->device, errp);
if (!blk) { if (!bs) {
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);
return; return;
} }
@ -1961,22 +1926,17 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
} }
/* AioContext is released in .clean() */ /* 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)) { if (state->aio_context != bdrv_get_aio_context(target)) {
state->aio_context = NULL; state->aio_context = NULL;
error_setg(errp, "Backup between two IO threads is not implemented"); error_setg(errp, "Backup between two IO threads is not implemented");
return; return;
} }
aio_context_acquire(state->aio_context); aio_context_acquire(state->aio_context);
state->bs = blk_bs(blk); state->bs = bs;
bdrv_drained_begin(state->bs); bdrv_drained_begin(state->bs);
do_blockdev_backup(backup->has_job_id ? backup->job_id : NULL, do_blockdev_backup(backup, common->block_job_txn, &local_err);
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);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; 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, bool has_on_error, BlockdevOnError on_error,
Error **errp) Error **errp)
{ {
BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
BlockDriverState *base_bs = NULL; BlockDriverState *base_bs = NULL;
AioContext *aio_context; 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; on_error = BLOCKDEV_ON_ERROR_REPORT;
} }
blk = blk_by_name(device); bs = qmp_get_root_bs(device, errp);
if (!blk) { if (!bs) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", device);
return; return;
} }
aio_context = blk_get_aio_context(blk); aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); 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)) { if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) {
goto out; 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, bool has_speed, int64_t speed,
Error **errp) Error **errp)
{ {
BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
BlockDriverState *base_bs, *top_bs; BlockDriverState *base_bs, *top_bs;
AioContext *aio_context; 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 * 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 * perform the device lookup before any generic errors that may occur in a
* scenario in which all optional arguments are omitted. */ * scenario in which all optional arguments are omitted. */
blk = blk_by_name(device); bs = qmp_get_root_bs(device, &local_err);
if (!blk) { if (!bs) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, bs = bdrv_lookup_bs(device, device, NULL);
"Device '%s' not found", device); 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; return;
} }
aio_context = blk_get_aio_context(blk); aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); 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)) { if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, errp)) {
goto out; goto out;
} }
@ -3155,19 +3105,8 @@ out:
aio_context_release(aio_context); aio_context_release(aio_context);
} }
static void do_drive_backup(const char *job_id, const char *device, static void do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, Error **errp)
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)
{ {
BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
BlockDriverState *target_bs; BlockDriverState *target_bs;
BlockDriverState *source = NULL; BlockDriverState *source = NULL;
@ -3178,39 +3117,36 @@ static void do_drive_backup(const char *job_id, const char *device,
int flags; int flags;
int64_t size; int64_t size;
if (!has_speed) { if (!backup->has_speed) {
speed = 0; backup->speed = 0;
} }
if (!has_on_source_error) { if (!backup->has_on_source_error) {
on_source_error = BLOCKDEV_ON_ERROR_REPORT; backup->on_source_error = BLOCKDEV_ON_ERROR_REPORT;
} }
if (!has_on_target_error) { if (!backup->has_on_target_error) {
on_target_error = BLOCKDEV_ON_ERROR_REPORT; backup->on_target_error = BLOCKDEV_ON_ERROR_REPORT;
} }
if (!has_mode) { if (!backup->has_mode) {
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; 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); bs = qmp_get_root_bs(backup->device, errp);
if (!blk) { if (!bs) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", device);
return; return;
} }
aio_context = blk_get_aio_context(blk); aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
/* Although backup_run has this check too, we need to use bs->drv below, so if (!backup->has_format) {
* do an early check redundantly. */ backup->format = backup->mode == NEW_IMAGE_MODE_EXISTING ?
if (!blk_is_available(blk)) { NULL : (char*) bs->drv->format_name;
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;
} }
/* Early check to avoid creating target */ /* 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 /* See if we have a backing HD we can use to create our new image
* on top of. */ * on top of. */
if (sync == MIRROR_SYNC_MODE_TOP) { if (backup->sync == MIRROR_SYNC_MODE_TOP) {
source = backing_bs(bs); source = backing_bs(bs);
if (!source) { 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; source = bs;
} }
@ -3238,14 +3174,14 @@ static void do_drive_backup(const char *job_id, const char *device,
goto out; goto out;
} }
if (mode != NEW_IMAGE_MODE_EXISTING) { if (backup->mode != NEW_IMAGE_MODE_EXISTING) {
assert(format); assert(backup->format);
if (source) { if (source) {
bdrv_img_create(target, format, source->filename, bdrv_img_create(backup->target, backup->format, source->filename,
source->drv->format_name, NULL, source->drv->format_name, NULL,
size, flags, &local_err, false); size, flags, &local_err, false);
} else { } else {
bdrv_img_create(target, format, NULL, NULL, NULL, bdrv_img_create(backup->target, backup->format, NULL, NULL, NULL,
size, flags, &local_err, false); size, flags, &local_err, false);
} }
} }
@ -3255,30 +3191,30 @@ static void do_drive_backup(const char *job_id, const char *device,
goto out; goto out;
} }
if (format) { if (backup->format) {
options = qdict_new(); 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) { if (!target_bs) {
goto out; goto out;
} }
bdrv_set_aio_context(target_bs, aio_context); bdrv_set_aio_context(target_bs, aio_context);
if (has_bitmap) { if (backup->has_bitmap) {
bmap = bdrv_find_dirty_bitmap(bs, bitmap); bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
if (!bmap) { 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); bdrv_unref(target_bs);
goto out; goto out;
} }
} }
backup_start(job_id, bs, target_bs, speed, sync, bmap, backup_start(backup->job_id, bs, target_bs, backup->speed, backup->sync,
on_source_error, on_target_error, bmap, backup->compress, backup->on_source_error,
block_job_cb, bs, txn, &local_err); backup->on_target_error, block_job_cb, bs, txn, &local_err);
bdrv_unref(target_bs); bdrv_unref(target_bs);
if (local_err != NULL) { if (local_err != NULL) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
@ -3289,24 +3225,9 @@ out:
aio_context_release(aio_context); aio_context_release(aio_context);
} }
void qmp_drive_backup(bool has_job_id, const char *job_id, void qmp_drive_backup(DriveBackup *arg, Error **errp)
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)
{ {
return do_drive_backup(has_job_id ? job_id : NULL, device, target, return do_drive_backup(arg, NULL, errp);
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);
} }
BlockDeviceInfoList *qmp_query_named_block_nodes(Error **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); return bdrv_named_nodes_list(errp);
} }
void do_blockdev_backup(const char *job_id, const char *device, void do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, Error **errp)
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)
{ {
BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
BlockDriverState *target_bs; BlockDriverState *target_bs;
Error *local_err = NULL; Error *local_err = NULL;
AioContext *aio_context; AioContext *aio_context;
if (!has_speed) { if (!backup->has_speed) {
speed = 0; backup->speed = 0;
} }
if (!has_on_source_error) { if (!backup->has_on_source_error) {
on_source_error = BLOCKDEV_ON_ERROR_REPORT; backup->on_source_error = BLOCKDEV_ON_ERROR_REPORT;
} }
if (!has_on_target_error) { if (!backup->has_on_target_error) {
on_target_error = BLOCKDEV_ON_ERROR_REPORT; 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); bs = qmp_get_root_bs(backup->device, errp);
if (!blk) { if (!bs) {
error_setg(errp, "Device '%s' not found", device);
return; return;
} }
aio_context = blk_get_aio_context(blk); aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
if (!blk_is_available(blk)) { target_bs = bdrv_lookup_bs(backup->target, backup->target, errp);
error_setg(errp, "Device '%s' has no medium", device);
goto out;
}
bs = blk_bs(blk);
target_bs = bdrv_lookup_bs(target, target, errp);
if (!target_bs) { if (!target_bs) {
goto out; goto out;
} }
@ -3370,8 +3282,9 @@ void do_blockdev_backup(const char *job_id, const char *device,
goto out; goto out;
} }
} }
backup_start(job_id, bs, target_bs, speed, sync, NULL, on_source_error, backup_start(backup->job_id, bs, target_bs, backup->speed, backup->sync,
on_target_error, block_job_cb, bs, txn, &local_err); NULL, backup->compress, backup->on_source_error,
backup->on_target_error, block_job_cb, bs, txn, &local_err);
if (local_err != NULL) { if (local_err != NULL) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
} }
@ -3379,21 +3292,9 @@ out:
aio_context_release(aio_context); aio_context_release(aio_context);
} }
void qmp_blockdev_backup(bool has_job_id, const char *job_id, void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp)
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)
{ {
do_blockdev_backup(has_job_id ? job_id : NULL, device, target, do_blockdev_backup(arg, NULL, errp);
sync, has_speed, speed,
has_on_source_error, on_source_error,
has_on_target_error, on_target_error,
NULL, errp);
} }
/* Parameter check and block job starting for drive mirroring. /* 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) void qmp_drive_mirror(DriveMirror *arg, Error **errp)
{ {
BlockDriverState *bs; BlockDriverState *bs;
BlockBackend *blk;
BlockDriverState *source, *target_bs; BlockDriverState *source, *target_bs;
AioContext *aio_context; AioContext *aio_context;
BlockMirrorBackingMode backing_mode; BlockMirrorBackingMode backing_mode;
@ -3479,21 +3379,14 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
int64_t size; int64_t size;
const char *format = arg->format; const char *format = arg->format;
blk = blk_by_name(arg->device); bs = qmp_get_root_bs(arg->device, errp);
if (!blk) { if (!bs) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", arg->device);
return; return;
} }
aio_context = blk_get_aio_context(blk); aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); 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) { if (!arg->has_mode) {
arg->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; 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) Error **errp)
{ {
BlockDriverState *bs; BlockDriverState *bs;
BlockBackend *blk;
BlockDriverState *target_bs; BlockDriverState *target_bs;
AioContext *aio_context; AioContext *aio_context;
BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN; BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN;
Error *local_err = NULL; Error *local_err = NULL;
blk = blk_by_name(device); bs = qmp_get_root_bs(device, errp);
if (!blk) {
error_setg(errp, "Device '%s' not found", device);
return;
}
bs = blk_bs(blk);
if (!bs) { if (!bs) {
error_setg(errp, "Device '%s' has no media", device);
return; return;
} }
@ -3785,7 +3670,6 @@ void qmp_change_backing_file(const char *device,
const char *backing_file, const char *backing_file,
Error **errp) Error **errp)
{ {
BlockBackend *blk;
BlockDriverState *bs = NULL; BlockDriverState *bs = NULL;
AioContext *aio_context; AioContext *aio_context;
BlockDriverState *image_bs = NULL; BlockDriverState *image_bs = NULL;
@ -3794,22 +3678,14 @@ void qmp_change_backing_file(const char *device,
int open_flags; int open_flags;
int ret; int ret;
blk = blk_by_name(device); bs = qmp_get_root_bs(device, errp);
if (!blk) { if (!bs) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", device);
return; return;
} }
aio_context = blk_get_aio_context(blk); aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); 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); image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, 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) { if (job_id == NULL) {
job_id = bdrv_get_device_name(bs); 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)) { if (!id_wellformed(job_id)) {

View File

@ -1182,8 +1182,8 @@ ETEXI
{ {
.name = "drive_backup", .name = "drive_backup",
.args_type = "reuse:-n,full:-f,device:B,target:s,format:s?", .args_type = "reuse:-n,full:-f,compress:-c,device:B,target:s,format:s?",
.params = "[-n] [-f] device target [format]", .params = "[-n] [-f] [-c] device target [format]",
.help = "initiates a point-in-time\n\t\t\t" .help = "initiates a point-in-time\n\t\t\t"
"copy for a device. The device's contents are\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" "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" "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" "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" "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, .mhandler.cmd = hmp_drive_backup,
}, },
STEXI 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"); const char *format = qdict_get_try_str(qdict, "format");
bool reuse = qdict_get_try_bool(qdict, "reuse", false); bool reuse = qdict_get_try_bool(qdict, "reuse", false);
bool full = qdict_get_try_bool(qdict, "full", 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; 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) { if (!filename) {
error_setg(&err, QERR_MISSING_PARAMETER, "target"); error_setg(&err, QERR_MISSING_PARAMETER, "target");
@ -1118,16 +1129,7 @@ void hmp_drive_backup(Monitor *mon, const QDict *qdict)
return; return;
} }
if (reuse) { qmp_drive_backup(&backup, &err);
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);
hmp_handle_error(mon, &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) void hmp_qemu_io(Monitor *mon, const QDict *qdict)
{ {
BlockBackend *blk; BlockBackend *blk;
BlockBackend *local_blk = NULL;
const char* device = qdict_get_str(qdict, "device"); const char* device = qdict_get_str(qdict, "device");
const char* command = qdict_get_str(qdict, "command"); const char* command = qdict_get_str(qdict, "command");
Error *err = NULL; Error *err = NULL;
blk = blk_by_name(device); 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) { if (blk) {
AioContext *aio_context = blk_get_aio_context(blk); AioContext *aio_context = blk_get_aio_context(blk);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
@ -1938,6 +1951,8 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
"Device '%s' not found", device); "Device '%s' not found", device);
} }
fail:
blk_unref(local_blk);
hmp_handle_error(mon, &err); 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); IDEDeviceClass *dc = IDE_DEVICE_GET_CLASS(dev);
IDEBus *bus = DO_UPCAST(IDEBus, qbus, qdev->parent_bus); IDEBus *bus = DO_UPCAST(IDEBus, qbus, qdev->parent_bus);
if (!dev->conf.blk) {
error_report("No drive specified");
goto err;
}
if (dev->unit == -1) { if (dev->unit == -1) {
dev->unit = bus->master ? 1 : 0; 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; IDEState *s = bus->ifs + dev->unit;
Error *err = NULL; 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) { if (dev->conf.discard_granularity == -1) {
dev->conf.discard_granularity = 512; dev->conf.discard_granularity = 512;
} else if (dev->conf.discard_granularity && } else if (dev->conf.discard_granularity &&
@ -257,7 +263,11 @@ static int ide_cd_initfn(IDEDevice *dev)
static int ide_drive_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); 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) static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
{ {
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
if (!dev->conf.blk) {
dev->conf.blk = blk_new();
}
s->qdev.blocksize = 2048; s->qdev.blocksize = 2048;
s->qdev.type = TYPE_ROM; s->qdev.type = TYPE_ROM;
s->features |= 1 << SCSI_DISK_F_REMOVABLE; s->features |= 1 << SCSI_DISK_F_REMOVABLE;

View File

@ -65,9 +65,10 @@ typedef enum {
BDRV_REQ_MAY_UNMAP = 0x4, BDRV_REQ_MAY_UNMAP = 0x4,
BDRV_REQ_NO_SERIALISING = 0x8, BDRV_REQ_NO_SERIALISING = 0x8,
BDRV_REQ_FUA = 0x10, BDRV_REQ_FUA = 0x10,
BDRV_REQ_WRITE_COMPRESSED = 0x20,
/* Mask of valid flags */ /* Mask of valid flags */
BDRV_REQ_MASK = 0x1f, BDRV_REQ_MASK = 0x3f,
} BdrvRequestFlags; } BdrvRequestFlags;
typedef struct BlockSizes { 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_name(const BlockDriverState *bs);
const char *bdrv_get_device_or_node_name(const BlockDriverState *bs); const char *bdrv_get_device_or_node_name(const BlockDriverState *bs);
int bdrv_get_flags(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); int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs); ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs);
void bdrv_round_sectors_to_clusters(BlockDriverState *bs, void bdrv_round_sectors_to_clusters(BlockDriverState *bs,

View File

@ -204,8 +204,8 @@ struct BlockDriver {
bool has_variable_length; bool has_variable_length;
int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs); int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs);
int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num, int coroutine_fn (*bdrv_co_pwritev_compressed)(BlockDriverState *bs,
const uint8_t *buf, int nb_sectors); uint64_t offset, uint64_t bytes, QEMUIOVector *qiov);
int (*bdrv_snapshot_create)(BlockDriverState *bs, int (*bdrv_snapshot_create)(BlockDriverState *bs,
QEMUSnapshotInfo *sn_info); 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, void backup_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *target, int64_t speed, BlockDriverState *target, int64_t speed,
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap, MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
bool compress,
BlockdevOnError on_source_error, BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
BlockCompletionFunc *cb, void *opaque, BlockCompletionFunc *cb, void *opaque,

View File

@ -103,8 +103,9 @@ int nbd_disconnect(int fd);
typedef struct NBDExport NBDExport; typedef struct NBDExport NBDExport;
typedef struct NBDClient NBDClient; 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 *), uint16_t nbdflags, void (*close)(NBDExport *),
bool writethrough, BlockBackend *on_eject_blk,
Error **errp); Error **errp);
void nbd_export_close(NBDExport *exp); void nbd_export_close(NBDExport *exp);
void nbd_export_get(NBDExport *exp); void nbd_export_get(NBDExport *exp);

View File

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

View File

@ -39,6 +39,7 @@ struct Coroutine {
void *entry_arg; void *entry_arg;
Coroutine *caller; Coroutine *caller;
QSLIST_ENTRY(Coroutine) pool_next; QSLIST_ENTRY(Coroutine) pool_next;
size_t locks_held;
/* Coroutines that should be woken up when we yield or terminate */ /* Coroutines that should be woken up when we yield or terminate */
QSIMPLEQ_HEAD(, Coroutine) co_queue_wakeup; 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_remove_bs(BlockBackend *blk);
void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs); void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs);
bool bdrv_has_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_set_allow_write_beyond_eof(BlockBackend *blk, bool allow);
void blk_iostatus_enable(BlockBackend *blk); void blk_iostatus_enable(BlockBackend *blk);
@ -203,8 +204,8 @@ void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk,
BlockCompletionFunc *cb, void *opaque); BlockCompletionFunc *cb, void *opaque);
int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset,
int count, BdrvRequestFlags flags); int count, BdrvRequestFlags flags);
int blk_write_compressed(BlockBackend *blk, int64_t sector_num, int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf,
const uint8_t *buf, int nb_sectors); int count);
int blk_truncate(BlockBackend *blk, int64_t offset); int blk_truncate(BlockBackend *blk, int64_t offset);
int blk_pdiscard(BlockBackend *blk, int64_t offset, int count); int blk_pdiscard(BlockBackend *blk, int64_t offset, int count);
int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,

View File

@ -69,6 +69,7 @@ struct NBDExport {
AioContext *ctx; AioContext *ctx;
BlockBackend *eject_notifier_blk;
Notifier eject_notifier; Notifier eject_notifier;
}; };
@ -807,11 +808,18 @@ static void nbd_eject_notifier(Notifier *n, void *data)
nbd_export_close(exp); 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 *), uint16_t nbdflags, void (*close)(NBDExport *),
bool writethrough, BlockBackend *on_eject_blk,
Error **errp) Error **errp)
{ {
BlockBackend *blk;
NBDExport *exp = g_malloc0(sizeof(NBDExport)); NBDExport *exp = g_malloc0(sizeof(NBDExport));
blk = blk_new();
blk_insert_bs(blk, bs);
blk_set_enable_write_cache(blk, !writethrough);
exp->refcount = 1; exp->refcount = 1;
QTAILQ_INIT(&exp->clients); QTAILQ_INIT(&exp->clients);
exp->blk = blk; exp->blk = blk;
@ -827,11 +835,14 @@ NBDExport *nbd_export_new(BlockBackend *blk, off_t dev_offset, off_t size,
exp->close = close; exp->close = close;
exp->ctx = blk_get_aio_context(blk); exp->ctx = blk_get_aio_context(blk);
blk_ref(blk);
blk_add_aio_context_notifier(blk, blk_aio_attached, blk_aio_detach, exp); blk_add_aio_context_notifier(blk, blk_aio_attached, blk_aio_detach, exp);
exp->eject_notifier.notify = nbd_eject_notifier; if (on_eject_blk) {
blk_add_remove_bs_notifier(blk, &exp->eject_notifier); 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 * 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; return exp;
fail: fail:
blk_unref(blk);
g_free(exp); g_free(exp);
return NULL; return NULL;
} }
@ -914,7 +926,10 @@ void nbd_export_put(NBDExport *exp)
} }
if (exp->blk) { 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_remove_aio_context_notifier(exp->blk, blk_aio_attached,
blk_aio_detach, exp); blk_aio_detach, exp);
blk_unref(exp->blk); blk_unref(exp->blk);

View File

@ -876,7 +876,7 @@
# @job-id: #optional identifier for the newly-created block job. If # @job-id: #optional identifier for the newly-created block job. If
# omitted, the device name will be used. (Since 2.7) # 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 # @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 # 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 # Must be present if sync is "incremental", must NOT be present
# otherwise. (Since 2.4) # 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, # @on-source-error: #optional the action to take on an error on the source,
# default 'report'. 'stop' and 'enospc' can only be used # default 'report'. 'stop' and 'enospc' can only be used
# if the block device supports io-status (see BlockInfo). # if the block device supports io-status (see BlockInfo).
@ -915,7 +918,7 @@
{ 'struct': 'DriveBackup', { 'struct': 'DriveBackup',
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str', 'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
'*format': 'str', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode', '*format': 'str', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
'*speed': 'int', '*bitmap': 'str', '*speed': 'int', '*bitmap': 'str', '*compress': 'bool',
'*on-source-error': 'BlockdevOnError', '*on-source-error': 'BlockdevOnError',
'*on-target-error': 'BlockdevOnError' } } '*on-target-error': 'BlockdevOnError' } }
@ -925,7 +928,7 @@
# @job-id: #optional identifier for the newly-created block job. If # @job-id: #optional identifier for the newly-created block job. If
# omitted, the device name will be used. (Since 2.7) # 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. # @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, # @speed: #optional the maximum speed, in bytes per second. The default is 0,
# for unlimited. # 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, # @on-source-error: #optional the action to take on an error on the source,
# default 'report'. 'stop' and 'enospc' can only be used # default 'report'. 'stop' and 'enospc' can only be used
# if the block device supports io-status (see BlockInfo). # if the block device supports io-status (see BlockInfo).
@ -954,6 +960,7 @@
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str', 'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
'sync': 'MirrorSyncMode', 'sync': 'MirrorSyncMode',
'*speed': 'int', '*speed': 'int',
'*compress': 'bool',
'*on-source-error': 'BlockdevOnError', '*on-source-error': 'BlockdevOnError',
'*on-target-error': 'BlockdevOnError' } } '*on-target-error': 'BlockdevOnError' } }
@ -998,7 +1005,8 @@
# @image-node-name: The name of the block driver state node of the # @image-node-name: The name of the block driver state node of the
# image to modify. # 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 # @backing-file: The string to write as the backing file. This
# string is not validated, so care should be taken # string is not validated, so care should be taken
@ -1020,7 +1028,7 @@
# @job-id: #optional identifier for the newly-created block job. If # @job-id: #optional identifier for the newly-created block job. If
# omitted, the device name will be used. (Since 2.7) # 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. # @base: #optional The file name of the backing image to write data into.
# If not specified, this is the deepest backing image # If not specified, this is the deepest backing image
@ -1086,11 +1094,12 @@
# For the arguments, see the documentation of DriveBackup. # For the arguments, see the documentation of DriveBackup.
# #
# Returns: nothing on success # 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 # Since 1.6
## ##
{ 'command': 'drive-backup', 'data': 'DriveBackup' } { 'command': 'drive-backup', 'boxed': true,
'data': 'DriveBackup' }
## ##
# @blockdev-backup # @blockdev-backup
@ -1103,9 +1112,13 @@
# #
# For the arguments, see the documentation of BlockdevBackup. # For the arguments, see the documentation of BlockdevBackup.
# #
# Returns: nothing on success
# If @device is not a valid block device, DeviceNotFound
#
# Since 2.3 # Since 2.3
## ##
{ 'command': 'blockdev-backup', 'data': 'BlockdevBackup' } { 'command': 'blockdev-backup', 'boxed': true,
'data': 'BlockdevBackup' }
## ##
@ -1127,7 +1140,7 @@
# See DriveMirror for parameter descriptions # See DriveMirror for parameter descriptions
# #
# Returns: nothing on success # 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 # Since 1.3
## ##
@ -1142,7 +1155,8 @@
# @job-id: #optional identifier for the newly-created block job. If # @job-id: #optional identifier for the newly-created block job. If
# omitted, the device name will be used. (Since 2.7) # 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 # @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 # 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 # @job-id: #optional identifier for the newly-created block job. If
# omitted, the device name will be used. (Since 2.7) # 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 # @target: the id or node-name of the block device to mirror to. This mustn't be
# attached to guest. # attached to guest.
@ -1462,7 +1477,7 @@
# @job-id: #optional identifier for the newly-created block job. If # @job-id: #optional identifier for the newly-created block job. If
# omitted, the device name will be used. (Since 2.7) # 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 # @base: #optional the common backing file name
# #
@ -1487,9 +1502,6 @@
# 'stop' and 'enospc' can only be used if the block device # 'stop' and 'enospc' can only be used if the block device
# supports io-status (see BlockInfo). Since 1.3. # supports io-status (see BlockInfo). Since 1.3.
# #
# Returns: Nothing on success
# If @device does not exist, DeviceNotFound
#
# Since: 1.1 # Since: 1.1
## ##
{ 'command': 'block-stream', { 'command': 'block-stream',

View File

@ -58,7 +58,8 @@
## ##
# @BlockdevSnapshotInternal # @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 # @name: the name of the internal snapshot to be created
# #
@ -80,7 +81,7 @@
# For the arguments, see the documentation of BlockdevSnapshotInternal. # For the arguments, see the documentation of BlockdevSnapshotInternal.
# #
# Returns: nothing on success # 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, # If any snapshot matching @name exists, or @name is empty,
# GenericError # GenericError
# If the format of the image used does not support it, # 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 # both. One of the name or id is required. Return SnapshotInfo for the
# successfully deleted snapshot. # 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 # @id: optional the snapshot's ID to be deleted
# #
# @name: optional the snapshot's name to be deleted # @name: optional the snapshot's name to be deleted
# #
# Returns: SnapshotInfo on success # 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 snapshot not found, GenericError
# If the format of the image used does not support it, # If the format of the image used does not support it,
# BlockFormatFeatureNotSupported # BlockFormatFeatureNotSupported
@ -159,9 +161,9 @@
## ##
# @nbd-server-add: # @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 # @writable: Whether clients should be able to write to the device via the
# NBD connection (default false). #optional # 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; 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) { if (ret < 0) {
return ret; return ret;
} }
@ -1727,7 +1729,7 @@ static int convert_do_copy(ImgConvertState *s)
if (s->compressed) { if (s->compressed) {
/* signal EOF to align */ /* 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) { if (ret < 0) {
goto fail; goto fail;
} }
@ -2032,7 +2034,7 @@ static int img_convert(int argc, char **argv)
const char *preallocation = const char *preallocation =
qemu_opt_get(opts, BLOCK_OPT_PREALLOC); 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"); error_report("Compression not supported for this file format");
ret = -1; ret = -1;
goto out; goto out;

View File

@ -504,7 +504,7 @@ static int do_write_compressed(BlockBackend *blk, char *buf, int64_t offset,
return -ERANGE; 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) { if (ret < 0) {
return ret; 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, exp = nbd_export_new(bs, dev_offset, fd_size, nbdflags, nbd_export_closed,
&local_err); writethrough, NULL, &local_err);
if (!exp) { if (!exp) {
error_report_err(local_err); error_report_err(local_err);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);

View File

@ -1120,7 +1120,7 @@ Arguments:
- "job-id": Identifier for the newly-created block job. If omitted, - "job-id": Identifier for the newly-created block job. If omitted,
the device name will be used. (json-string, optional) 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 - "base": The file name of the backing image above which copying starts
(json-string, optional) (json-string, optional)
- "backing-file": The backing file string to write into the active layer. This - "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, - "job-id": Identifier for the newly-created block job. If omitted,
the device name will be used. (json-string, optional) 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. - "base": The file name of the backing image to write data into.
If not specified, this is the deepest backing image If not specified, this is the deepest backing image
(json-string, optional) (json-string, optional)
@ -1217,7 +1217,8 @@ EQMP
{ {
.name = "drive-backup", .name = "drive-backup",
.args_type = "job-id:s?,sync:s,device:B,target:s,speed:i?,mode:s?," .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, .mhandler.cmd_new = qmp_marshal_drive_backup,
}, },
@ -1235,7 +1236,7 @@ Arguments:
- "job-id": Identifier for the newly-created block job. If omitted, - "job-id": Identifier for the newly-created block job. If omitted,
the device name will be used. (json-string, optional) 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) (json-string)
- "target": the target of the new image. If the file exists, or if it is a - "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 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 - "mode": whether and how QEMU should create a new image
(NewImageMode, optional, default 'absolute-paths') (NewImageMode, optional, default 'absolute-paths')
- "speed": the maximum speed, in bytes per second (json-int, optional) - "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 - "on-source-error": the action to take on an error on the source, default
'report'. 'stop' and 'enospc' can only be used 'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status. if the block device supports io-status.
@ -1272,7 +1275,7 @@ EQMP
{ {
.name = "blockdev-backup", .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?", "on-source-error:s?,on-target-error:s?",
.mhandler.cmd_new = qmp_marshal_blockdev_backup, .mhandler.cmd_new = qmp_marshal_blockdev_backup,
}, },
@ -1288,7 +1291,7 @@ Arguments:
- "job-id": Identifier for the newly-created block job. If omitted, - "job-id": Identifier for the newly-created block job. If omitted,
the device name will be used. (json-string, optional) 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) (json-string)
- "target": the name of the backup target device. (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; - "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 sectors allocated in the topmost image, or "none" to only replicate
new I/O (MirrorSyncMode). new I/O (MirrorSyncMode).
- "speed": the maximum speed, in bytes per second (json-int, optional) - "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 - "on-source-error": the action to take on an error on the source, default
'report'. 'stop' and 'enospc' can only be used 'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status. if the block device supports io-status.
@ -1407,7 +1412,8 @@ actions array:
- "mode": whether and how QEMU should create the snapshot file - "mode": whether and how QEMU should create the snapshot file
(NewImageMode, optional, default "absolute-paths") (NewImageMode, optional, default "absolute-paths")
When "type" is "blockdev-snapshot-internal-sync": 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) - "name": name of the new snapshot (json-string)
Example: Example:
@ -1608,7 +1614,8 @@ name already exists, the operation will fail.
Arguments: 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) - "name": name of the new snapshot (json-string)
Example: Example:
@ -1639,7 +1646,7 @@ fail.
Arguments: 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) - "id": ID of the snapshot (json-string, optional)
- "name": name 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, - "job-id": Identifier for the newly-created block job. If omitted,
the device name will be used. (json-string, optional) 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) - "target": name of new image file (json-string)
- "format": format of new image (json-string, optional) - "format": format of new image (json-string, optional)
- "node-name": the name of the new block driver state in the node graph - "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, - "job-id": Identifier for the newly-created block job. If omitted,
the device name will be used. (json-string, optional) 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) - "target": device name to mirror to (json-string)
- "replaces": the block driver node name to replace when finished - "replaces": the block driver node name to replace when finished
(json-string, optional) (json-string, optional)
@ -1803,7 +1812,8 @@ Arguments:
"device". "device".
(json-string, optional) (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) (json-string)
- "backing-file": The string to write as the backing file. This string is - "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): def test_device_not_found(self):
result = self.vm.qmp('block-stream', device='nonexistent') 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): class TestSmallerBackingFile(iotests.QMPTestCase):

View File

@ -38,7 +38,6 @@ class TestSingleDrive(iotests.QMPTestCase):
image_len = 1 * 1024 * 1024 # MB image_len = 1 * 1024 * 1024 # MB
qmp_cmd = 'drive-mirror' qmp_cmd = 'drive-mirror'
qmp_target = target_img qmp_target = target_img
not_found_error = 'DeviceNotFound'
def setUp(self): def setUp(self):
iotests.create_image(backing_img, self.image_len) 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', result = self.vm.qmp(self.qmp_cmd, device='ide1-cd0', sync='full',
target=self.qmp_target) 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): def test_image_not_found(self):
result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', 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): def test_device_not_found(self):
result = self.vm.qmp(self.qmp_cmd, device='nonexistent', sync='full', result = self.vm.qmp(self.qmp_cmd, device='nonexistent', sync='full',
target=self.qmp_target) target=self.qmp_target)
self.assert_qmp(result, 'error/class', self.not_found_error) self.assert_qmp(result, 'error/class', 'GenericError')
class TestSingleBlockdev(TestSingleDrive): class TestSingleBlockdev(TestSingleDrive):
qmp_cmd = 'blockdev-mirror' qmp_cmd = 'blockdev-mirror'
qmp_target = 'node1' qmp_target = 'node1'
not_found_error = 'GenericError'
def setUp(self): def setUp(self):
TestSingleDrive.setUp(self) TestSingleDrive.setUp(self)
@ -922,7 +920,7 @@ class TestRepairQuorum(iotests.QMPTestCase):
node_name='repair0', node_name='repair0',
replaces='img1', replaces='img1',
target=quorum_repair_img, format=iotests.imgfmt) 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): def test_wrong_sync_mode(self):
if not self.has_quorum(): if not self.has_quorum():

View File

@ -134,10 +134,7 @@ class TestSingleDrive(iotests.QMPTestCase):
def do_test_device_not_found(self, cmd, **args): def do_test_device_not_found(self, cmd, **args):
result = self.vm.qmp(cmd, **args) result = self.vm.qmp(cmd, **args)
if cmd == 'drive-backup': self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/class', 'DeviceNotFound')
else:
self.assert_qmp(result, 'error/class', 'GenericError')
def test_device_not_found(self): def test_device_not_found(self):
self.do_test_device_not_found('drive-backup', device='nonexistent', self.do_test_device_not_found('drive-backup', device='nonexistent',
@ -371,7 +368,7 @@ class TestSingleTransaction(iotests.QMPTestCase):
'sync': 'full' }, 'sync': 'full' },
} }
]) ])
self.assert_qmp(result, 'error/class', 'DeviceNotFound') self.assert_qmp(result, 'error/class', 'GenericError')
result = self.vm.qmp('transaction', actions=[{ result = self.vm.qmp('transaction', actions=[{
'type': 'blockdev-backup', 'type': 'blockdev-backup',
@ -451,5 +448,123 @@ class TestSingleTransaction(iotests.QMPTestCase):
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_no_active_block_jobs() 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__': if __name__ == '__main__':
iotests.main(supported_fmts=['raw', 'qcow2']) iotests.main(supported_fmts=['raw', 'qcow2'])

View File

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

View File

@ -182,7 +182,7 @@ class TestSingleTransaction(ImageSnapshotTestCase):
'name': 'a' }, 'name': 'a' },
}] }]
result = self.vm.qmp('transaction', actions = actions) 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): def test_error_exist(self):
self.createSnapshotInTransaction(1) self.createSnapshotInTransaction(1)
@ -239,7 +239,7 @@ class TestSnapshotDelete(ImageSnapshotTestCase):
result = self.vm.qmp('blockdev-snapshot-delete-internal-sync', result = self.vm.qmp('blockdev-snapshot-delete-internal-sync',
device = 'drive_error', device = 'drive_error',
id = '0') id = '0')
self.assert_qmp(result, 'error/class', 'DeviceNotFound') self.assert_qmp(result, 'error/class', 'GenericError')
def test_error_no_id_and_name(self): def test_error_no_id_and_name(self):
result = self.vm.qmp('blockdev-snapshot-delete-internal-sync', 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') qemu_default_machine = os.environ.get('QEMU_DEFAULT_MACHINE')
socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper') socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper')
debug = False
def qemu_img(*args): def qemu_img(*args):
'''Run qemu-img and return the exit code''' '''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))) sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args)))
return subp.communicate()[0] 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 True if two image files are identical'''
return qemu_img('compare', '-f', imgfmt, return qemu_img('compare', '-f', fmt1,
'-F', imgfmt, img1, img2) == 0 '-F', fmt2, img1, img2) == 0
def create_image(name, size): def create_image(name, size):
'''Create a fully-allocated raw image with sector markers''' '''Create a fully-allocated raw image with sector markers'''
@ -134,6 +135,8 @@ class VM(qtest.QEMUQtestMachine):
def __init__(self): def __init__(self):
super(VM, self).__init__(qemu_prog, qemu_opts, test_dir=test_dir, super(VM, self).__init__(qemu_prog, qemu_opts, test_dir=test_dir,
socket_scm_helper=socket_scm_helper) socket_scm_helper=socket_scm_helper)
if debug:
self._debug = True
self._num_drives = 0 self._num_drives = 0
def add_drive_raw(self, opts): def add_drive_raw(self, opts):
@ -141,14 +144,14 @@ class VM(qtest.QEMUQtestMachine):
self._args.append(opts) self._args.append(opts)
return self 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''' '''Add a virtio-blk drive to the VM'''
options = ['if=%s' % interface, options = ['if=%s' % interface,
'id=drive%d' % self._num_drives] 'id=drive%d' % self._num_drives]
if path is not None: if path is not None:
options.append('file=%s' % path) options.append('file=%s' % path)
options.append('format=%s' % imgfmt) options.append('format=%s' % format)
options.append('cache=%s' % cachemode) options.append('cache=%s' % cachemode)
if opts: if opts:
@ -318,6 +321,8 @@ def verify_quorum():
def main(supported_fmts=[], supported_oses=['linux']): def main(supported_fmts=[], supported_oses=['linux']):
'''Run tests''' '''Run tests'''
global debug
# We are using TEST_DIR and QEMU_DEFAULT_MACHINE as proxies to # We are using TEST_DIR and QEMU_DEFAULT_MACHINE as proxies to
# indicate that we're not being run via "check". There may be # indicate that we're not being run via "check". There may be
# other things set up by "check" that individual test cases rely # 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 *c1;
Coroutine *c2; Coroutine *c2;
Coroutine tmp;
c2 = qemu_coroutine_create(c2_fn, NULL); c2 = qemu_coroutine_create(c2_fn, NULL);
c1 = qemu_coroutine_create(c1_fn, c2); c1 = qemu_coroutine_create(c1_fn, c2);
qemu_coroutine_enter(c1); 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)); memset(c1, 0xff, sizeof(Coroutine));
qemu_coroutine_enter(c2); 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->locked = true;
mutex->holder = self;
self->locks_held++;
trace_qemu_co_mutex_lock_return(mutex, self); 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); trace_qemu_co_mutex_unlock_entry(mutex, self);
assert(mutex->locked == true); assert(mutex->locked == true);
assert(mutex->holder == self);
assert(qemu_in_coroutine()); assert(qemu_in_coroutine());
mutex->locked = false; mutex->locked = false;
mutex->holder = NULL;
self->locks_held--;
qemu_co_queue_next(&mutex->queue); qemu_co_queue_next(&mutex->queue);
trace_qemu_co_mutex_unlock_return(mutex, self); 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) void qemu_co_rwlock_rdlock(CoRwlock *lock)
{ {
Coroutine *self = qemu_coroutine_self();
while (lock->writer) { while (lock->writer) {
qemu_co_queue_wait(&lock->queue); qemu_co_queue_wait(&lock->queue);
} }
lock->reader++; lock->reader++;
self->locks_held++;
} }
void qemu_co_rwlock_unlock(CoRwlock *lock) void qemu_co_rwlock_unlock(CoRwlock *lock)
{ {
Coroutine *self = qemu_coroutine_self();
assert(qemu_in_coroutine()); assert(qemu_in_coroutine());
if (lock->writer) { if (lock->writer) {
lock->writer = false; lock->writer = false;
@ -176,12 +186,16 @@ void qemu_co_rwlock_unlock(CoRwlock *lock)
qemu_co_queue_next(&lock->queue); qemu_co_queue_next(&lock->queue);
} }
} }
self->locks_held--;
} }
void qemu_co_rwlock_wrlock(CoRwlock *lock) void qemu_co_rwlock_wrlock(CoRwlock *lock)
{ {
Coroutine *self = qemu_coroutine_self();
while (lock->writer || lock->reader) { while (lock->writer || lock->reader) {
qemu_co_queue_wait(&lock->queue); qemu_co_queue_wait(&lock->queue);
} }
lock->writer = true; lock->writer = true;
self->locks_held++;
} }

View File

@ -122,6 +122,7 @@ void qemu_coroutine_enter(Coroutine *co)
case COROUTINE_YIELD: case COROUTINE_YIELD:
return; return;
case COROUTINE_TERMINATE: case COROUTINE_TERMINATE:
assert(!co->locks_held);
trace_qemu_coroutine_terminate(co); trace_qemu_coroutine_terminate(co);
coroutine_delete(co); coroutine_delete(co);
return; return;