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