mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-27 05:20:50 +00:00
Merge remote-tracking branch 'stefanha/block' into staging
# By Paolo Bonzini (17) and others # Via Stefan Hajnoczi * stefanha/block: (48 commits) qemu-iotests: filter QEMU monitor \r\n aio: make aio_poll(ctx, true) block with no fds block: clean up bdrv_drain_all() throttling comments qcow2: use start_of_cluster() and offset_into_cluster() everywhere qemu-img: decrease progress update interval on convert qemu-img: round down request length to an aligned sector qemu-img: dynamically adjust iobuffer size during convert block/iscsi: set bs->bl.opt_transfer_length block: add opt_transfer_length to BlockLimits block/iscsi: set bdi->cluster_size qemu-img: fix usage instruction for qemu-img convert qemu-img: add support for skipping zeroes in input during convert qemu-nbd: add doc for option -f qemu-iotests: add test for snapshot in qemu-img convert qemu-img: add -l for snapshot in convert qemu-iotests: add 058 internal snapshot export with qemu-nbd case qemu-nbd: support internal snapshot export snapshot: distinguish id and name in load_tmp qemu-iotests: Split qcow2 only cases in 048 qemu-iotests: Clean up spaces in usage output ... Message-id: 1386347807-27359-1-git-send-email-stefanha@redhat.com Signed-off-by: Anthony Liguori <aliguori@amazon.com>
This commit is contained in:
commit
93531372f0
@ -217,11 +217,6 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
|
||||
ctx->walking_handlers--;
|
||||
|
||||
/* early return if we only have the aio_notify() fd */
|
||||
if (ctx->pollfds->len == 1) {
|
||||
return progress;
|
||||
}
|
||||
|
||||
/* wait until next event */
|
||||
ret = qemu_poll_ns((GPollFD *)ctx->pollfds->data,
|
||||
ctx->pollfds->len,
|
||||
|
@ -161,11 +161,6 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
|
||||
ctx->walking_handlers--;
|
||||
|
||||
/* early return if we only have the aio_notify() fd */
|
||||
if (count == 1) {
|
||||
return progress;
|
||||
}
|
||||
|
||||
/* wait until next event */
|
||||
while (count > 0) {
|
||||
int ret;
|
||||
|
176
block.c
176
block.c
@ -79,6 +79,7 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BdrvRequestFlags flags,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque,
|
||||
bool is_write);
|
||||
@ -1556,13 +1557,8 @@ void bdrv_drain_all(void)
|
||||
BlockDriverState *bs;
|
||||
|
||||
while (busy) {
|
||||
/* FIXME: We do not have timer support here, so this is effectively
|
||||
* a busy wait.
|
||||
*/
|
||||
QTAILQ_FOREACH(bs, &bdrv_states, list) {
|
||||
if (bdrv_start_throttled_reqs(bs)) {
|
||||
busy = true;
|
||||
}
|
||||
bdrv_start_throttled_reqs(bs);
|
||||
}
|
||||
|
||||
busy = bdrv_requests_pending_all();
|
||||
@ -2770,14 +2766,21 @@ static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs,
|
||||
while (nb_sectors > 0 && !ret) {
|
||||
int num = nb_sectors;
|
||||
|
||||
/* align request */
|
||||
if (bs->bl.write_zeroes_alignment &&
|
||||
num >= bs->bl.write_zeroes_alignment &&
|
||||
sector_num % bs->bl.write_zeroes_alignment) {
|
||||
if (num > bs->bl.write_zeroes_alignment) {
|
||||
/* Align request. Block drivers can expect the "bulk" of the request
|
||||
* to be aligned.
|
||||
*/
|
||||
if (bs->bl.write_zeroes_alignment
|
||||
&& num > bs->bl.write_zeroes_alignment) {
|
||||
if (sector_num % bs->bl.write_zeroes_alignment != 0) {
|
||||
/* Make a small request up to the first aligned sector. */
|
||||
num = bs->bl.write_zeroes_alignment;
|
||||
num -= sector_num % bs->bl.write_zeroes_alignment;
|
||||
} else if ((sector_num + num) % bs->bl.write_zeroes_alignment != 0) {
|
||||
/* Shorten the request to the last aligned sector. num cannot
|
||||
* underflow because num > bs->bl.write_zeroes_alignment.
|
||||
*/
|
||||
num -= (sector_num + num) % bs->bl.write_zeroes_alignment;
|
||||
}
|
||||
num -= sector_num % bs->bl.write_zeroes_alignment;
|
||||
}
|
||||
|
||||
/* limit request size */
|
||||
@ -2795,16 +2798,20 @@ static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs,
|
||||
/* Fall back to bounce buffer if write zeroes is unsupported */
|
||||
iov.iov_len = num * BDRV_SECTOR_SIZE;
|
||||
if (iov.iov_base == NULL) {
|
||||
/* allocate bounce buffer only once and ensure that it
|
||||
* is big enough for this and all future requests.
|
||||
*/
|
||||
size_t bufsize = num <= nb_sectors ? num : max_write_zeroes;
|
||||
iov.iov_base = qemu_blockalign(bs, bufsize * BDRV_SECTOR_SIZE);
|
||||
memset(iov.iov_base, 0, bufsize * BDRV_SECTOR_SIZE);
|
||||
iov.iov_base = qemu_blockalign(bs, num * BDRV_SECTOR_SIZE);
|
||||
memset(iov.iov_base, 0, num * BDRV_SECTOR_SIZE);
|
||||
}
|
||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
||||
|
||||
ret = drv->bdrv_co_writev(bs, sector_num, num, &qiov);
|
||||
|
||||
/* Keep bounce buffer around if it is big enough for all
|
||||
* all future requests.
|
||||
*/
|
||||
if (num < max_write_zeroes) {
|
||||
qemu_vfree(iov.iov_base);
|
||||
iov.iov_base = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
sector_num += num;
|
||||
@ -2887,7 +2894,7 @@ int coroutine_fn bdrv_co_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
trace_bdrv_co_write_zeroes(bs, sector_num, nb_sectors);
|
||||
trace_bdrv_co_write_zeroes(bs, sector_num, nb_sectors, flags);
|
||||
|
||||
if (!(bs->open_flags & BDRV_O_UNMAP)) {
|
||||
flags &= ~BDRV_REQ_MAY_UNMAP;
|
||||
@ -3669,7 +3676,7 @@ BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
{
|
||||
trace_bdrv_aio_readv(bs, sector_num, nb_sectors, opaque);
|
||||
|
||||
return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors,
|
||||
return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors, 0,
|
||||
cb, opaque, false);
|
||||
}
|
||||
|
||||
@ -3679,7 +3686,18 @@ BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
{
|
||||
trace_bdrv_aio_writev(bs, sector_num, nb_sectors, opaque);
|
||||
|
||||
return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors,
|
||||
return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors, 0,
|
||||
cb, opaque, true);
|
||||
}
|
||||
|
||||
BlockDriverAIOCB *bdrv_aio_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
trace_bdrv_aio_write_zeroes(bs, sector_num, nb_sectors, flags, opaque);
|
||||
|
||||
return bdrv_co_aio_rw_vector(bs, sector_num, NULL, nb_sectors,
|
||||
BDRV_REQ_ZERO_WRITE | flags,
|
||||
cb, opaque, true);
|
||||
}
|
||||
|
||||
@ -3851,8 +3869,10 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
|
||||
/* Run the aio requests. */
|
||||
mcb->num_requests = num_reqs;
|
||||
for (i = 0; i < num_reqs; i++) {
|
||||
bdrv_aio_writev(bs, reqs[i].sector, reqs[i].qiov,
|
||||
reqs[i].nb_sectors, multiwrite_cb, mcb);
|
||||
bdrv_co_aio_rw_vector(bs, reqs[i].sector, reqs[i].qiov,
|
||||
reqs[i].nb_sectors, reqs[i].flags,
|
||||
multiwrite_cb, mcb,
|
||||
true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -3994,10 +4014,10 @@ static void coroutine_fn bdrv_co_do_rw(void *opaque)
|
||||
|
||||
if (!acb->is_write) {
|
||||
acb->req.error = bdrv_co_do_readv(bs, acb->req.sector,
|
||||
acb->req.nb_sectors, acb->req.qiov, 0);
|
||||
acb->req.nb_sectors, acb->req.qiov, acb->req.flags);
|
||||
} else {
|
||||
acb->req.error = bdrv_co_do_writev(bs, acb->req.sector,
|
||||
acb->req.nb_sectors, acb->req.qiov, 0);
|
||||
acb->req.nb_sectors, acb->req.qiov, acb->req.flags);
|
||||
}
|
||||
|
||||
acb->bh = qemu_bh_new(bdrv_co_em_bh, acb);
|
||||
@ -4008,6 +4028,7 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BdrvRequestFlags flags,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque,
|
||||
bool is_write)
|
||||
@ -4019,6 +4040,7 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
|
||||
acb->req.sector = sector_num;
|
||||
acb->req.nb_sectors = nb_sectors;
|
||||
acb->req.qiov = qiov;
|
||||
acb->req.flags = flags;
|
||||
acb->is_write = is_write;
|
||||
acb->done = NULL;
|
||||
|
||||
@ -4302,6 +4324,8 @@ static void coroutine_fn bdrv_discard_co_entry(void *opaque)
|
||||
int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
int max_discard;
|
||||
|
||||
if (!bs->drv) {
|
||||
return -ENOMEDIUM;
|
||||
} else if (bdrv_check_request(bs, sector_num, nb_sectors)) {
|
||||
@ -4317,55 +4341,55 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bs->drv->bdrv_co_discard) {
|
||||
int max_discard = bs->bl.max_discard ?
|
||||
bs->bl.max_discard : MAX_DISCARD_DEFAULT;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
int ret;
|
||||
int num = nb_sectors;
|
||||
|
||||
/* align request */
|
||||
if (bs->bl.discard_alignment &&
|
||||
num >= bs->bl.discard_alignment &&
|
||||
sector_num % bs->bl.discard_alignment) {
|
||||
if (num > bs->bl.discard_alignment) {
|
||||
num = bs->bl.discard_alignment;
|
||||
}
|
||||
num -= sector_num % bs->bl.discard_alignment;
|
||||
}
|
||||
|
||||
/* limit request size */
|
||||
if (num > max_discard) {
|
||||
num = max_discard;
|
||||
}
|
||||
|
||||
ret = bs->drv->bdrv_co_discard(bs, sector_num, num);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sector_num += num;
|
||||
nb_sectors -= num;
|
||||
}
|
||||
return 0;
|
||||
} else if (bs->drv->bdrv_aio_discard) {
|
||||
BlockDriverAIOCB *acb;
|
||||
CoroutineIOCompletion co = {
|
||||
.coroutine = qemu_coroutine_self(),
|
||||
};
|
||||
|
||||
acb = bs->drv->bdrv_aio_discard(bs, sector_num, nb_sectors,
|
||||
bdrv_co_io_em_complete, &co);
|
||||
if (acb == NULL) {
|
||||
return -EIO;
|
||||
} else {
|
||||
qemu_coroutine_yield();
|
||||
return co.ret;
|
||||
}
|
||||
} else {
|
||||
if (!bs->drv->bdrv_co_discard && !bs->drv->bdrv_aio_discard) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
max_discard = bs->bl.max_discard ? bs->bl.max_discard : MAX_DISCARD_DEFAULT;
|
||||
while (nb_sectors > 0) {
|
||||
int ret;
|
||||
int num = nb_sectors;
|
||||
|
||||
/* align request */
|
||||
if (bs->bl.discard_alignment &&
|
||||
num >= bs->bl.discard_alignment &&
|
||||
sector_num % bs->bl.discard_alignment) {
|
||||
if (num > bs->bl.discard_alignment) {
|
||||
num = bs->bl.discard_alignment;
|
||||
}
|
||||
num -= sector_num % bs->bl.discard_alignment;
|
||||
}
|
||||
|
||||
/* limit request size */
|
||||
if (num > max_discard) {
|
||||
num = max_discard;
|
||||
}
|
||||
|
||||
if (bs->drv->bdrv_co_discard) {
|
||||
ret = bs->drv->bdrv_co_discard(bs, sector_num, num);
|
||||
} else {
|
||||
BlockDriverAIOCB *acb;
|
||||
CoroutineIOCompletion co = {
|
||||
.coroutine = qemu_coroutine_self(),
|
||||
};
|
||||
|
||||
acb = bs->drv->bdrv_aio_discard(bs, sector_num, nb_sectors,
|
||||
bdrv_co_io_em_complete, &co);
|
||||
if (acb == NULL) {
|
||||
return -EIO;
|
||||
} else {
|
||||
qemu_coroutine_yield();
|
||||
ret = co.ret;
|
||||
}
|
||||
}
|
||||
if (ret && ret != -ENOTSUP) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sector_num += num;
|
||||
nb_sectors -= num;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
|
||||
@ -4684,7 +4708,6 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||
{
|
||||
QEMUOptionParameter *param = NULL, *create_options = NULL;
|
||||
QEMUOptionParameter *backing_fmt, *backing_file, *size;
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockDriver *drv, *proto_drv;
|
||||
BlockDriver *backing_drv = NULL;
|
||||
Error *local_err = NULL;
|
||||
@ -4763,6 +4786,7 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||
size = get_option_parameter(param, BLOCK_OPT_SIZE);
|
||||
if (size && size->value.n == -1) {
|
||||
if (backing_file && backing_file->value.s) {
|
||||
BlockDriverState *bs;
|
||||
uint64_t size;
|
||||
char buf[32];
|
||||
int back_flags;
|
||||
@ -4781,6 +4805,7 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
local_err = NULL;
|
||||
bdrv_unref(bs);
|
||||
goto out;
|
||||
}
|
||||
bdrv_get_geometry(bs, &size);
|
||||
@ -4788,6 +4813,8 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||
|
||||
snprintf(buf, sizeof(buf), "%" PRId64, size);
|
||||
set_option_parameter(param, BLOCK_OPT_SIZE, buf);
|
||||
|
||||
bdrv_unref(bs);
|
||||
} else {
|
||||
error_setg(errp, "Image creation needs a size parameter");
|
||||
goto out;
|
||||
@ -4818,9 +4845,6 @@ out:
|
||||
free_option_parameters(create_options);
|
||||
free_option_parameters(param);
|
||||
|
||||
if (bs) {
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
* QEMU Block driver for iSCSI images
|
||||
*
|
||||
* Copyright (c) 2010-2011 Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
* Copyright (c) 2012-2013 Peter Lieven <pl@kamp.de>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -54,6 +55,7 @@ typedef struct IscsiLun {
|
||||
QEMUTimer *nop_timer;
|
||||
uint8_t lbpme;
|
||||
uint8_t lbprz;
|
||||
uint8_t has_write_same;
|
||||
struct scsi_inquiry_logical_block_provisioning lbp;
|
||||
struct scsi_inquiry_block_limits bl;
|
||||
unsigned char *zeroblock;
|
||||
@ -975,8 +977,13 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!iscsilun->lbp.lbpws) {
|
||||
/* WRITE SAME is not supported by the target */
|
||||
if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) {
|
||||
/* WRITE SAME without UNMAP is not supported by the target */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if ((flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->lbp.lbpws) {
|
||||
/* WRITE SAME with UNMAP is not supported by the target */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
@ -1011,6 +1018,14 @@ retry:
|
||||
}
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
if (iTask.status == SCSI_STATUS_CHECK_CONDITION &&
|
||||
iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST &&
|
||||
iTask.task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) {
|
||||
/* WRITE SAME is not supported by the target */
|
||||
iscsilun->has_write_same = false;
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -1374,6 +1389,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
iscsilun->type = inq->periperal_device_type;
|
||||
iscsilun->has_write_same = true;
|
||||
|
||||
if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
|
||||
goto out;
|
||||
@ -1441,6 +1457,9 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
bs->bl.write_zeroes_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran,
|
||||
iscsilun);
|
||||
|
||||
bs->bl.opt_transfer_length = sector_lun2qemu(iscsilun->bl.opt_xfer_len,
|
||||
iscsilun);
|
||||
}
|
||||
|
||||
#if defined(LIBISCSI_FEATURE_NOP_COUNTER)
|
||||
@ -1505,11 +1524,6 @@ static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iscsi_has_zero_init(BlockDriverState *bs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iscsi_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
@ -1569,6 +1583,13 @@ static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
bdi->unallocated_blocks_are_zero = !!iscsilun->lbprz;
|
||||
bdi->can_write_zeroes_with_unmap = iscsilun->lbprz && iscsilun->lbp.lbpws;
|
||||
/* Guess the internal cluster (page) size of the iscsi target by the means
|
||||
* of opt_unmap_gran. Transfer the unmap granularity only if it has a
|
||||
* reasonable size for bdi->cluster_size */
|
||||
if (iscsilun->bl.opt_unmap_gran * iscsilun->block_size >= 64 * 1024 &&
|
||||
iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) {
|
||||
bdi->cluster_size = iscsilun->bl.opt_unmap_gran * iscsilun->block_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1608,8 +1629,6 @@ static BlockDriver bdrv_iscsi = {
|
||||
.bdrv_aio_writev = iscsi_aio_writev,
|
||||
.bdrv_aio_flush = iscsi_aio_flush,
|
||||
|
||||
.bdrv_has_zero_init = iscsi_has_zero_init,
|
||||
|
||||
#ifdef __linux__
|
||||
.bdrv_ioctl = iscsi_ioctl,
|
||||
.bdrv_aio_ioctl = iscsi_aio_ioctl,
|
||||
|
@ -1401,7 +1401,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
|
||||
/* Round start up and end down */
|
||||
offset = align_offset(offset, s->cluster_size);
|
||||
end_offset &= ~(s->cluster_size - 1);
|
||||
end_offset = start_of_cluster(s, end_offset);
|
||||
|
||||
if (offset > end_offset) {
|
||||
return 0;
|
||||
|
@ -515,8 +515,8 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
||||
s->l2_table_cache);
|
||||
}
|
||||
|
||||
start = offset & ~(s->cluster_size - 1);
|
||||
last = (offset + length - 1) & ~(s->cluster_size - 1);
|
||||
start = start_of_cluster(s, offset);
|
||||
last = start_of_cluster(s, offset + length - 1);
|
||||
for(cluster_offset = start; cluster_offset <= last;
|
||||
cluster_offset += s->cluster_size)
|
||||
{
|
||||
@ -724,7 +724,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
}
|
||||
redo:
|
||||
free_in_cluster = s->cluster_size -
|
||||
(s->free_byte_offset & (s->cluster_size - 1));
|
||||
offset_into_cluster(s, s->free_byte_offset);
|
||||
if (size <= free_in_cluster) {
|
||||
/* enough space in current cluster */
|
||||
offset = s->free_byte_offset;
|
||||
@ -732,7 +732,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
free_in_cluster -= size;
|
||||
if (free_in_cluster == 0)
|
||||
s->free_byte_offset = 0;
|
||||
if ((offset & (s->cluster_size - 1)) != 0)
|
||||
if (offset_into_cluster(s, offset) != 0)
|
||||
qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
|
||||
QCOW2_DISCARD_NEVER);
|
||||
} else {
|
||||
@ -740,7 +740,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
if (offset < 0) {
|
||||
return offset;
|
||||
}
|
||||
cluster_offset = s->free_byte_offset & ~(s->cluster_size - 1);
|
||||
cluster_offset = start_of_cluster(s, s->free_byte_offset);
|
||||
if ((cluster_offset + s->cluster_size) == offset) {
|
||||
/* we are lucky: contiguous data */
|
||||
offset = s->free_byte_offset;
|
||||
@ -1010,8 +1010,8 @@ static void inc_refcounts(BlockDriverState *bs,
|
||||
if (size <= 0)
|
||||
return;
|
||||
|
||||
start = offset & ~(s->cluster_size - 1);
|
||||
last = (offset + size - 1) & ~(s->cluster_size - 1);
|
||||
start = start_of_cluster(s, offset);
|
||||
last = start_of_cluster(s, offset + size - 1);
|
||||
for(cluster_offset = start; cluster_offset <= last;
|
||||
cluster_offset += s->cluster_size) {
|
||||
k = cluster_offset >> s->cluster_bits;
|
||||
@ -1122,7 +1122,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
offset, s->cluster_size);
|
||||
|
||||
/* Correct offsets are cluster aligned */
|
||||
if (offset & (s->cluster_size - 1)) {
|
||||
if (offset_into_cluster(s, offset)) {
|
||||
fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not "
|
||||
"properly aligned; L2 entry corrupted.\n", offset);
|
||||
res->corruptions++;
|
||||
@ -1194,7 +1194,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
|
||||
l2_offset, s->cluster_size);
|
||||
|
||||
/* L2 tables are cluster aligned */
|
||||
if (l2_offset & (s->cluster_size - 1)) {
|
||||
if (offset_into_cluster(s, l2_offset)) {
|
||||
fprintf(stderr, "ERROR l2_offset=%" PRIx64 ": Table is not "
|
||||
"cluster aligned; L1 entry corrupted\n", l2_offset);
|
||||
res->corruptions++;
|
||||
@ -1423,7 +1423,7 @@ static int64_t realloc_refcount_block(BlockDriverState *bs, int reftable_index,
|
||||
}
|
||||
|
||||
/* update refcount table */
|
||||
assert(!(new_offset & (s->cluster_size - 1)));
|
||||
assert(!offset_into_cluster(s, new_offset));
|
||||
s->refcount_table[reftable_index] = new_offset;
|
||||
ret = write_reftable_entry(bs, reftable_index);
|
||||
if (ret < 0) {
|
||||
@ -1507,7 +1507,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
cluster = offset >> s->cluster_bits;
|
||||
|
||||
/* Refcount blocks are cluster aligned */
|
||||
if (offset & (s->cluster_size - 1)) {
|
||||
if (offset_into_cluster(s, offset)) {
|
||||
fprintf(stderr, "ERROR refcount block %" PRId64 " is not "
|
||||
"cluster aligned; refcount table entry corrupted\n", i);
|
||||
res->corruptions++;
|
||||
|
@ -675,7 +675,10 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
||||
return s->nb_snapshots;
|
||||
}
|
||||
|
||||
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
|
||||
int qcow2_snapshot_load_tmp(BlockDriverState *bs,
|
||||
const char *snapshot_id,
|
||||
const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
int i, snapshot_index;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
@ -687,8 +690,10 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
|
||||
assert(bs->read_only);
|
||||
|
||||
/* Search the snapshot */
|
||||
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_name);
|
||||
snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name);
|
||||
if (snapshot_index < 0) {
|
||||
error_setg(errp,
|
||||
"Can't find snapshot");
|
||||
return -ENOENT;
|
||||
}
|
||||
sn = &s->snapshots[snapshot_index];
|
||||
@ -699,6 +704,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
|
||||
|
||||
ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_table, new_l1_bytes);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to read l1 table for snapshot");
|
||||
g_free(new_l1_table);
|
||||
return ret;
|
||||
}
|
||||
|
@ -718,6 +718,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
qemu_opts_del(opts);
|
||||
bs->bl.write_zeroes_alignment = s->cluster_sectors;
|
||||
|
||||
if (s->use_lazy_refcounts && s->qcow_version < 3) {
|
||||
error_setg(errp, "Lazy refcounts require a qcow2 image with at least "
|
||||
@ -1471,7 +1472,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
* size for any qcow2 image.
|
||||
*/
|
||||
BlockDriverState* bs;
|
||||
QCowHeader header;
|
||||
QCowHeader *header;
|
||||
uint8_t* refcount_table;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
@ -1489,30 +1490,34 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
}
|
||||
|
||||
/* Write the header */
|
||||
memset(&header, 0, sizeof(header));
|
||||
header.magic = cpu_to_be32(QCOW_MAGIC);
|
||||
header.version = cpu_to_be32(version);
|
||||
header.cluster_bits = cpu_to_be32(cluster_bits);
|
||||
header.size = cpu_to_be64(0);
|
||||
header.l1_table_offset = cpu_to_be64(0);
|
||||
header.l1_size = cpu_to_be32(0);
|
||||
header.refcount_table_offset = cpu_to_be64(cluster_size);
|
||||
header.refcount_table_clusters = cpu_to_be32(1);
|
||||
header.refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT);
|
||||
header.header_length = cpu_to_be32(sizeof(header));
|
||||
QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
|
||||
header = g_malloc0(cluster_size);
|
||||
*header = (QCowHeader) {
|
||||
.magic = cpu_to_be32(QCOW_MAGIC),
|
||||
.version = cpu_to_be32(version),
|
||||
.cluster_bits = cpu_to_be32(cluster_bits),
|
||||
.size = cpu_to_be64(0),
|
||||
.l1_table_offset = cpu_to_be64(0),
|
||||
.l1_size = cpu_to_be32(0),
|
||||
.refcount_table_offset = cpu_to_be64(cluster_size),
|
||||
.refcount_table_clusters = cpu_to_be32(1),
|
||||
.refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT),
|
||||
.header_length = cpu_to_be32(sizeof(*header)),
|
||||
};
|
||||
|
||||
if (flags & BLOCK_FLAG_ENCRYPT) {
|
||||
header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
|
||||
header->crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
|
||||
} else {
|
||||
header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
|
||||
header->crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
|
||||
}
|
||||
|
||||
if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) {
|
||||
header.compatible_features |=
|
||||
header->compatible_features |=
|
||||
cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(bs, 0, &header, sizeof(header));
|
||||
ret = bdrv_pwrite(bs, 0, header, cluster_size);
|
||||
g_free(header);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not write qcow2 header");
|
||||
goto out;
|
||||
@ -1893,6 +1898,8 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
|
||||
static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
bdi->unallocated_blocks_are_zero = true;
|
||||
bdi->can_write_zeroes_with_unmap = (s->qcow_version >= 3);
|
||||
bdi->cluster_size = s->cluster_size;
|
||||
bdi->vm_state_offset = qcow2_vm_state_offset(s);
|
||||
return 0;
|
||||
|
@ -488,7 +488,10 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
|
||||
const char *name,
|
||||
Error **errp);
|
||||
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab);
|
||||
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name);
|
||||
int qcow2_snapshot_load_tmp(BlockDriverState *bs,
|
||||
const char *snapshot_id,
|
||||
const char *name,
|
||||
Error **errp);
|
||||
|
||||
void qcow2_free_snapshots(BlockDriverState *bs);
|
||||
int qcow2_read_snapshots(BlockDriverState *bs);
|
||||
|
@ -495,6 +495,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
}
|
||||
|
||||
bs->bl.write_zeroes_alignment = s->header.cluster_size >> BDRV_SECTOR_BITS;
|
||||
s->need_check_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
qed_need_check_timer_cb, s);
|
||||
|
||||
@ -1475,6 +1476,8 @@ static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
memset(bdi, 0, sizeof(*bdi));
|
||||
bdi->cluster_size = s->header.cluster_size;
|
||||
bdi->is_dirty = s->header.features & QED_F_NEED_CHECK;
|
||||
bdi->unallocated_blocks_are_zero = true;
|
||||
bdi->can_write_zeroes_with_unmap = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -21,9 +21,10 @@
|
||||
#define QEMU_AIO_IOCTL 0x0004
|
||||
#define QEMU_AIO_FLUSH 0x0008
|
||||
#define QEMU_AIO_DISCARD 0x0010
|
||||
#define QEMU_AIO_WRITE_ZEROES 0x0020
|
||||
#define QEMU_AIO_TYPE_MASK \
|
||||
(QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH| \
|
||||
QEMU_AIO_DISCARD)
|
||||
QEMU_AIO_DISCARD|QEMU_AIO_WRITE_ZEROES)
|
||||
|
||||
/* AIO flags */
|
||||
#define QEMU_AIO_MISALIGNED 0x1000
|
||||
|
@ -139,9 +139,11 @@ typedef struct BDRVRawState {
|
||||
void *aio_ctx;
|
||||
#endif
|
||||
#ifdef CONFIG_XFS
|
||||
bool is_xfs : 1;
|
||||
bool is_xfs:1;
|
||||
#endif
|
||||
bool has_discard : 1;
|
||||
bool has_discard:1;
|
||||
bool has_write_zeroes:1;
|
||||
bool discard_zeroes:1;
|
||||
} BDRVRawState;
|
||||
|
||||
typedef struct BDRVRawReopenState {
|
||||
@ -283,6 +285,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
Error *local_err = NULL;
|
||||
const char *filename;
|
||||
int fd, ret;
|
||||
struct stat st;
|
||||
|
||||
opts = qemu_opts_create_nofail(&raw_runtime_opts);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
@ -323,10 +326,38 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
}
|
||||
#endif
|
||||
|
||||
s->has_discard = 1;
|
||||
s->has_discard = true;
|
||||
s->has_write_zeroes = true;
|
||||
|
||||
if (fstat(s->fd, &st) < 0) {
|
||||
error_setg_errno(errp, errno, "Could not stat file");
|
||||
goto fail;
|
||||
}
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
s->discard_zeroes = true;
|
||||
}
|
||||
if (S_ISBLK(st.st_mode)) {
|
||||
#ifdef BLKDISCARDZEROES
|
||||
unsigned int arg;
|
||||
if (ioctl(s->fd, BLKDISCARDZEROES, &arg) == 0 && arg) {
|
||||
s->discard_zeroes = true;
|
||||
}
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
/* On Linux 3.10, BLKDISCARD leaves stale data in the page cache. Do
|
||||
* not rely on the contents of discarded blocks unless using O_DIRECT.
|
||||
* Same for BLKZEROOUT.
|
||||
*/
|
||||
if (!(bs->open_flags & BDRV_O_NOCACHE)) {
|
||||
s->discard_zeroes = false;
|
||||
s->has_write_zeroes = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_XFS
|
||||
if (platform_test_xfs_fd(s->fd)) {
|
||||
s->is_xfs = 1;
|
||||
s->is_xfs = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -675,6 +706,23 @@ static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_XFS
|
||||
static int xfs_write_zeroes(BDRVRawState *s, int64_t offset, uint64_t bytes)
|
||||
{
|
||||
struct xfs_flock64 fl;
|
||||
|
||||
memset(&fl, 0, sizeof(fl));
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = offset;
|
||||
fl.l_len = bytes;
|
||||
|
||||
if (xfsctl(NULL, s->fd, XFS_IOC_ZERO_RANGE, &fl) < 0) {
|
||||
DEBUG_BLOCK_PRINT("cannot write zero range (%s)\n", strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes)
|
||||
{
|
||||
struct xfs_flock64 fl;
|
||||
@ -693,13 +741,49 @@ static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes)
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb)
|
||||
{
|
||||
int ret = -EOPNOTSUPP;
|
||||
BDRVRawState *s = aiocb->bs->opaque;
|
||||
|
||||
if (s->has_write_zeroes == 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (aiocb->aio_type & QEMU_AIO_BLKDEV) {
|
||||
#ifdef BLKZEROOUT
|
||||
do {
|
||||
uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes };
|
||||
if (ioctl(aiocb->aio_fildes, BLKZEROOUT, range) == 0) {
|
||||
return 0;
|
||||
}
|
||||
} while (errno == EINTR);
|
||||
|
||||
ret = -errno;
|
||||
#endif
|
||||
} else {
|
||||
#ifdef CONFIG_XFS
|
||||
if (s->is_xfs) {
|
||||
return xfs_write_zeroes(s, aiocb->aio_offset, aiocb->aio_nbytes);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ret == -ENODEV || ret == -ENOSYS || ret == -EOPNOTSUPP ||
|
||||
ret == -ENOTTY) {
|
||||
s->has_write_zeroes = false;
|
||||
ret = -ENOTSUP;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb)
|
||||
{
|
||||
int ret = -EOPNOTSUPP;
|
||||
BDRVRawState *s = aiocb->bs->opaque;
|
||||
|
||||
if (s->has_discard == 0) {
|
||||
return 0;
|
||||
if (!s->has_discard) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (aiocb->aio_type & QEMU_AIO_BLKDEV) {
|
||||
@ -734,8 +818,8 @@ static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb)
|
||||
|
||||
if (ret == -ENODEV || ret == -ENOSYS || ret == -EOPNOTSUPP ||
|
||||
ret == -ENOTTY) {
|
||||
s->has_discard = 0;
|
||||
ret = 0;
|
||||
s->has_discard = false;
|
||||
ret = -ENOTSUP;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -777,6 +861,9 @@ static int aio_worker(void *arg)
|
||||
case QEMU_AIO_DISCARD:
|
||||
ret = handle_aiocb_discard(aiocb);
|
||||
break;
|
||||
case QEMU_AIO_WRITE_ZEROES:
|
||||
ret = handle_aiocb_write_zeroes(aiocb);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
|
||||
ret = -EINVAL;
|
||||
@ -787,6 +874,29 @@ static int aio_worker(void *arg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int paio_submit_co(BlockDriverState *bs, int fd,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
int type)
|
||||
{
|
||||
RawPosixAIOData *acb = g_slice_new(RawPosixAIOData);
|
||||
ThreadPool *pool;
|
||||
|
||||
acb->bs = bs;
|
||||
acb->aio_type = type;
|
||||
acb->aio_fildes = fd;
|
||||
|
||||
if (qiov) {
|
||||
acb->aio_iov = qiov->iov;
|
||||
acb->aio_niov = qiov->niov;
|
||||
}
|
||||
acb->aio_nbytes = nb_sectors * 512;
|
||||
acb->aio_offset = sector_num * 512;
|
||||
|
||||
trace_paio_submit_co(sector_num, nb_sectors, type);
|
||||
pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
|
||||
return thread_pool_submit_co(pool, aio_worker, acb);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type)
|
||||
@ -1199,6 +1309,31 @@ static coroutine_fn BlockDriverAIOCB *raw_aio_discard(BlockDriverState *bs,
|
||||
cb, opaque, QEMU_AIO_DISCARD);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_write_zeroes(
|
||||
BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, BdrvRequestFlags flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
if (!(flags & BDRV_REQ_MAY_UNMAP)) {
|
||||
return paio_submit_co(bs, s->fd, sector_num, NULL, nb_sectors,
|
||||
QEMU_AIO_WRITE_ZEROES);
|
||||
} else if (s->discard_zeroes) {
|
||||
return paio_submit_co(bs, s->fd, sector_num, NULL, nb_sectors,
|
||||
QEMU_AIO_DISCARD);
|
||||
}
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
bdi->unallocated_blocks_are_zero = s->discard_zeroes;
|
||||
bdi->can_write_zeroes_with_unmap = s->discard_zeroes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QEMUOptionParameter raw_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
@ -1222,6 +1357,7 @@ static BlockDriver bdrv_file = {
|
||||
.bdrv_create = raw_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_co_get_block_status = raw_co_get_block_status,
|
||||
.bdrv_co_write_zeroes = raw_co_write_zeroes,
|
||||
|
||||
.bdrv_aio_readv = raw_aio_readv,
|
||||
.bdrv_aio_writev = raw_aio_writev,
|
||||
@ -1230,6 +1366,7 @@ static BlockDriver bdrv_file = {
|
||||
|
||||
.bdrv_truncate = raw_truncate,
|
||||
.bdrv_getlength = raw_getlength,
|
||||
.bdrv_get_info = raw_get_info,
|
||||
.bdrv_get_allocated_file_size
|
||||
= raw_get_allocated_file_size,
|
||||
|
||||
@ -1525,6 +1662,26 @@ static coroutine_fn BlockDriverAIOCB *hdev_aio_discard(BlockDriverState *bs,
|
||||
cb, opaque, QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV);
|
||||
}
|
||||
|
||||
static coroutine_fn int hdev_co_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int rc;
|
||||
|
||||
rc = fd_open(bs);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
if (!(flags & BDRV_REQ_MAY_UNMAP)) {
|
||||
return paio_submit_co(bs, s->fd, sector_num, NULL, nb_sectors,
|
||||
QEMU_AIO_WRITE_ZEROES|QEMU_AIO_BLKDEV);
|
||||
} else if (s->discard_zeroes) {
|
||||
return paio_submit_co(bs, s->fd, sector_num, NULL, nb_sectors,
|
||||
QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV);
|
||||
}
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int hdev_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
@ -1577,6 +1734,7 @@ static BlockDriver bdrv_host_device = {
|
||||
.bdrv_reopen_abort = raw_reopen_abort,
|
||||
.bdrv_create = hdev_create,
|
||||
.create_options = raw_create_options,
|
||||
.bdrv_co_write_zeroes = hdev_co_write_zeroes,
|
||||
|
||||
.bdrv_aio_readv = raw_aio_readv,
|
||||
.bdrv_aio_writev = raw_aio_writev,
|
||||
@ -1585,6 +1743,7 @@ static BlockDriver bdrv_host_device = {
|
||||
|
||||
.bdrv_truncate = raw_truncate,
|
||||
.bdrv_getlength = raw_getlength,
|
||||
.bdrv_get_info = raw_get_info,
|
||||
.bdrv_get_allocated_file_size
|
||||
= raw_get_allocated_file_size,
|
||||
|
||||
|
@ -25,6 +25,24 @@
|
||||
#include "block/snapshot.h"
|
||||
#include "block/block_int.h"
|
||||
|
||||
QemuOptsList internal_snapshot_opts = {
|
||||
.name = "snapshot",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(internal_snapshot_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = SNAPSHOT_OPT_ID,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "snapshot id"
|
||||
},{
|
||||
.name = SNAPSHOT_OPT_NAME,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "snapshot name"
|
||||
},{
|
||||
/* end of list */
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
|
||||
const char *name)
|
||||
{
|
||||
@ -194,7 +212,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
||||
* If only @snapshot_id is specified, delete the first one with id
|
||||
* @snapshot_id.
|
||||
* If only @name is specified, delete the first one with name @name.
|
||||
* if none is specified, return -ENINVAL.
|
||||
* if none is specified, return -EINVAL.
|
||||
*
|
||||
* Returns: 0 on success, -errno on failure. If @bs is not inserted, return
|
||||
* -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs
|
||||
@ -265,18 +283,71 @@ int bdrv_snapshot_list(BlockDriverState *bs,
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily load an internal snapshot by @snapshot_id and @name.
|
||||
* @bs: block device used in the operation
|
||||
* @snapshot_id: unique snapshot ID, or NULL
|
||||
* @name: snapshot name, or NULL
|
||||
* @errp: location to store error
|
||||
*
|
||||
* If both @snapshot_id and @name are specified, load the first one with
|
||||
* id @snapshot_id and name @name.
|
||||
* If only @snapshot_id is specified, load the first one with id
|
||||
* @snapshot_id.
|
||||
* If only @name is specified, load the first one with name @name.
|
||||
* if none is specified, return -EINVAL.
|
||||
*
|
||||
* Returns: 0 on success, -errno on fail. If @bs is not inserted, return
|
||||
* -ENOMEDIUM. If @bs is not readonly, return -EINVAL. If @bs did not support
|
||||
* internal snapshot, return -ENOTSUP. If qemu can't find a matching @id and
|
||||
* @name, return -ENOENT. If @errp != NULL, it will always be filled on
|
||||
* failure.
|
||||
*/
|
||||
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
|
||||
const char *snapshot_name)
|
||||
const char *snapshot_id,
|
||||
const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
|
||||
if (!drv) {
|
||||
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs));
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
if (!snapshot_id && !name) {
|
||||
error_setg(errp, "snapshot_id and name are both NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!bs->read_only) {
|
||||
error_setg(errp, "Device is not readonly");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (drv->bdrv_snapshot_load_tmp) {
|
||||
return drv->bdrv_snapshot_load_tmp(bs, snapshot_name);
|
||||
return drv->bdrv_snapshot_load_tmp(bs, snapshot_id, name, errp);
|
||||
}
|
||||
error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
|
||||
drv->format_name, bdrv_get_device_name(bs),
|
||||
"temporarily load internal snapshot");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs,
|
||||
const char *id_or_name,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
Error *local_err = NULL;
|
||||
|
||||
ret = bdrv_snapshot_load_tmp(bs, id_or_name, NULL, &local_err);
|
||||
if (ret == -ENOENT || ret == -EINVAL) {
|
||||
error_free(local_err);
|
||||
local_err = NULL;
|
||||
ret = bdrv_snapshot_load_tmp(bs, NULL, id_or_name, &local_err);
|
||||
}
|
||||
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -331,6 +331,7 @@ static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
logout("\n");
|
||||
bdi->cluster_size = s->block_size;
|
||||
bdi->vm_state_offset = 0;
|
||||
bdi->unallocated_blocks_are_zero = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
13
block/vhdx.c
13
block/vhdx.c
@ -1043,6 +1043,18 @@ static void vhdx_block_translate(BDRVVHDXState *s, int64_t sector_num,
|
||||
}
|
||||
|
||||
|
||||
static int vhdx_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
BDRVVHDXState *s = bs->opaque;
|
||||
|
||||
bdi->cluster_size = s->block_size;
|
||||
|
||||
bdi->unallocated_blocks_are_zero =
|
||||
(s->params.data_bits & VHDX_PARAMS_HAS_PARENT) == 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
@ -1885,6 +1897,7 @@ static BlockDriver bdrv_vhdx = {
|
||||
.bdrv_co_readv = vhdx_co_readv,
|
||||
.bdrv_co_writev = vhdx_co_writev,
|
||||
.bdrv_create = vhdx_create,
|
||||
.bdrv_get_info = vhdx_get_info,
|
||||
|
||||
.create_options = vhdx_create_options,
|
||||
};
|
||||
|
66
block/vmdk.c
66
block/vmdk.c
@ -428,6 +428,10 @@ static int vmdk_add_extent(BlockDriverState *bs,
|
||||
extent->l2_size = l2_size;
|
||||
extent->cluster_sectors = flat ? sectors : cluster_sectors;
|
||||
|
||||
if (!flat) {
|
||||
bs->bl.write_zeroes_alignment =
|
||||
MAX(bs->bl.write_zeroes_alignment, cluster_sectors);
|
||||
}
|
||||
if (s->num_extents > 1) {
|
||||
extent->end_sector = (*(extent - 1)).end_sector + extent->sectors;
|
||||
} else {
|
||||
@ -1596,7 +1600,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
int fd, idx = 0;
|
||||
char desc[BUF_SIZE];
|
||||
char *desc = NULL;
|
||||
int64_t total_size = 0, filesize;
|
||||
const char *adapter_type = NULL;
|
||||
const char *backing_file = NULL;
|
||||
@ -1604,7 +1608,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
||||
int flags = 0;
|
||||
int ret = 0;
|
||||
bool flat, split, compress;
|
||||
char ext_desc_lines[BUF_SIZE] = "";
|
||||
GString *ext_desc_lines;
|
||||
char path[PATH_MAX], prefix[PATH_MAX], postfix[PATH_MAX];
|
||||
const int64_t split_size = 0x80000000; /* VMDK has constant split size */
|
||||
const char *desc_extent_line;
|
||||
@ -1632,8 +1636,11 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
||||
"ddb.geometry.sectors = \"63\"\n"
|
||||
"ddb.adapterType = \"%s\"\n";
|
||||
|
||||
ext_desc_lines = g_string_new(NULL);
|
||||
|
||||
if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) {
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
/* Read out options */
|
||||
while (options && options->name) {
|
||||
@ -1659,7 +1666,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
||||
strcmp(adapter_type, "lsilogic") &&
|
||||
strcmp(adapter_type, "legacyESX")) {
|
||||
error_setg(errp, "Unknown adapter type: '%s'", adapter_type);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
if (strcmp(adapter_type, "ide") != 0) {
|
||||
/* that's the number of heads with which vmware operates when
|
||||
@ -1675,7 +1683,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
||||
strcmp(fmt, "twoGbMaxExtentFlat") &&
|
||||
strcmp(fmt, "streamOptimized")) {
|
||||
error_setg(errp, "Unknown subformat: '%s'", fmt);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
split = !(strcmp(fmt, "twoGbMaxExtentFlat") &&
|
||||
strcmp(fmt, "twoGbMaxExtentSparse"));
|
||||
@ -1689,22 +1698,25 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
||||
}
|
||||
if (flat && backing_file) {
|
||||
error_setg(errp, "Flat image can't have backing file");
|
||||
return -ENOTSUP;
|
||||
ret = -ENOTSUP;
|
||||
goto exit;
|
||||
}
|
||||
if (flat && zeroed_grain) {
|
||||
error_setg(errp, "Flat image can't enable zeroed grain");
|
||||
return -ENOTSUP;
|
||||
ret = -ENOTSUP;
|
||||
goto exit;
|
||||
}
|
||||
if (backing_file) {
|
||||
BlockDriverState *bs = bdrv_new("");
|
||||
ret = bdrv_open(bs, backing_file, NULL, BDRV_O_NO_BACKING, NULL, errp);
|
||||
if (ret != 0) {
|
||||
bdrv_unref(bs);
|
||||
return ret;
|
||||
goto exit;
|
||||
}
|
||||
if (strcmp(bs->drv->format_name, "vmdk")) {
|
||||
bdrv_unref(bs);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
parent_cid = vmdk_read_cid(bs, 0);
|
||||
bdrv_unref(bs);
|
||||
@ -1738,25 +1750,27 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
||||
|
||||
if (vmdk_create_extent(ext_filename, size,
|
||||
flat, compress, zeroed_grain)) {
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
filesize -= size;
|
||||
|
||||
/* Format description line */
|
||||
snprintf(desc_line, sizeof(desc_line),
|
||||
desc_extent_line, size / 512, desc_filename);
|
||||
pstrcat(ext_desc_lines, sizeof(ext_desc_lines), desc_line);
|
||||
g_string_append(ext_desc_lines, desc_line);
|
||||
}
|
||||
/* generate descriptor file */
|
||||
snprintf(desc, sizeof(desc), desc_template,
|
||||
(unsigned int)time(NULL),
|
||||
parent_cid,
|
||||
fmt,
|
||||
parent_desc_line,
|
||||
ext_desc_lines,
|
||||
(flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
|
||||
total_size / (int64_t)(63 * number_heads * 512), number_heads,
|
||||
adapter_type);
|
||||
desc = g_strdup_printf(desc_template,
|
||||
(unsigned int)time(NULL),
|
||||
parent_cid,
|
||||
fmt,
|
||||
parent_desc_line,
|
||||
ext_desc_lines->str,
|
||||
(flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
|
||||
total_size / (int64_t)(63 * number_heads * 512),
|
||||
number_heads,
|
||||
adapter_type);
|
||||
if (split || flat) {
|
||||
fd = qemu_open(filename,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
@ -1767,21 +1781,25 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
||||
0644);
|
||||
}
|
||||
if (fd < 0) {
|
||||
return -errno;
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
/* the descriptor offset = 0x200 */
|
||||
if (!split && !flat && 0x200 != lseek(fd, 0x200, SEEK_SET)) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
goto close_exit;
|
||||
}
|
||||
ret = qemu_write_full(fd, desc, strlen(desc));
|
||||
if (ret != strlen(desc)) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
goto close_exit;
|
||||
}
|
||||
ret = 0;
|
||||
exit:
|
||||
close_exit:
|
||||
qemu_close(fd);
|
||||
exit:
|
||||
g_free(desc);
|
||||
g_string_free(ext_desc_lines, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
15
block/vpc.c
15
block/vpc.c
@ -455,6 +455,19 @@ fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int vpc_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
BDRVVPCState *s = (BDRVVPCState *)bs->opaque;
|
||||
VHDFooter *footer = (VHDFooter *) s->footer_buf;
|
||||
|
||||
if (cpu_to_be32(footer->type) != VHD_FIXED) {
|
||||
bdi->cluster_size = s->block_size;
|
||||
}
|
||||
|
||||
bdi->unallocated_blocks_are_zero = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpc_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
@ -857,6 +870,8 @@ static BlockDriver bdrv_vpc = {
|
||||
.bdrv_read = vpc_co_read,
|
||||
.bdrv_write = vpc_co_write,
|
||||
|
||||
.bdrv_get_info = vpc_get_info,
|
||||
|
||||
.create_options = vpc_create_options,
|
||||
.bdrv_has_zero_init = vpc_has_zero_init,
|
||||
};
|
||||
|
@ -41,6 +41,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
|
||||
#include <scsi/sg.h>
|
||||
#endif
|
||||
|
||||
#define SCSI_WRITE_SAME_MAX 524288
|
||||
#define SCSI_DMA_BUF_SIZE 131072
|
||||
#define SCSI_MAX_INQUIRY_LEN 256
|
||||
#define SCSI_MAX_MODE_LEN 256
|
||||
@ -634,6 +635,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
||||
buflen = 0x40;
|
||||
memset(outbuf + 4, 0, buflen - 4);
|
||||
|
||||
outbuf[4] = 0x1; /* wsnz */
|
||||
|
||||
/* optimal transfer length granularity */
|
||||
outbuf[6] = (min_io_size >> 8) & 0xff;
|
||||
outbuf[7] = min_io_size & 0xff;
|
||||
@ -1543,10 +1546,16 @@ done:
|
||||
|
||||
static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
uint8_t *p = inbuf;
|
||||
int len = r->req.cmd.xfer;
|
||||
UnmapCBData *data;
|
||||
|
||||
/* Reject ANCHOR=1. */
|
||||
if (r->req.cmd.buf[1] & 0x1) {
|
||||
goto invalid_field;
|
||||
}
|
||||
|
||||
if (len < 8) {
|
||||
goto invalid_param_len;
|
||||
}
|
||||
@ -1560,6 +1569,11 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
|
||||
goto invalid_param_len;
|
||||
}
|
||||
|
||||
if (bdrv_is_read_only(s->qdev.conf.bs)) {
|
||||
scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
|
||||
return;
|
||||
}
|
||||
|
||||
data = g_new0(UnmapCBData, 1);
|
||||
data->r = r;
|
||||
data->inbuf = &p[8];
|
||||
@ -1572,6 +1586,115 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
|
||||
|
||||
invalid_param_len:
|
||||
scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN));
|
||||
return;
|
||||
|
||||
invalid_field:
|
||||
scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
|
||||
}
|
||||
|
||||
typedef struct WriteSameCBData {
|
||||
SCSIDiskReq *r;
|
||||
int64_t sector;
|
||||
int nb_sectors;
|
||||
QEMUIOVector qiov;
|
||||
struct iovec iov;
|
||||
} WriteSameCBData;
|
||||
|
||||
static void scsi_write_same_complete(void *opaque, int ret)
|
||||
{
|
||||
WriteSameCBData *data = opaque;
|
||||
SCSIDiskReq *r = data->r;
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
|
||||
assert(r->req.aiocb != NULL);
|
||||
r->req.aiocb = NULL;
|
||||
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
|
||||
if (r->req.io_canceled) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
if (scsi_handle_rw_error(r, -ret)) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
data->nb_sectors -= data->iov.iov_len / 512;
|
||||
data->sector += data->iov.iov_len / 512;
|
||||
data->iov.iov_len = MIN(data->nb_sectors * 512, data->iov.iov_len);
|
||||
if (data->iov.iov_len) {
|
||||
bdrv_acct_start(s->qdev.conf.bs, &r->acct, data->iov.iov_len, BDRV_ACCT_WRITE);
|
||||
r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, data->sector,
|
||||
&data->qiov, data->iov.iov_len / 512,
|
||||
scsi_write_same_complete, r);
|
||||
return;
|
||||
}
|
||||
|
||||
scsi_req_complete(&r->req, GOOD);
|
||||
|
||||
done:
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
qemu_vfree(data->iov.iov_base);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
|
||||
{
|
||||
SCSIRequest *req = &r->req;
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
||||
uint32_t nb_sectors = scsi_data_cdb_length(r->req.cmd.buf);
|
||||
WriteSameCBData *data;
|
||||
uint8_t *buf;
|
||||
int i;
|
||||
|
||||
/* Fail if PBDATA=1 or LBDATA=1 or ANCHOR=1. */
|
||||
if (nb_sectors == 0 || (req->cmd.buf[1] & 0x16)) {
|
||||
scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_is_read_only(s->qdev.conf.bs)) {
|
||||
scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
|
||||
return;
|
||||
}
|
||||
if (!check_lba_range(s, r->req.cmd.lba, nb_sectors)) {
|
||||
scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer_is_zero(inbuf, s->qdev.blocksize)) {
|
||||
int flags = (req->cmd.buf[1] & 0x8) ? BDRV_REQ_MAY_UNMAP : 0;
|
||||
|
||||
/* The request is used as the AIO opaque value, so add a ref. */
|
||||
scsi_req_ref(&r->req);
|
||||
bdrv_acct_start(s->qdev.conf.bs, &r->acct, nb_sectors * s->qdev.blocksize,
|
||||
BDRV_ACCT_WRITE);
|
||||
r->req.aiocb = bdrv_aio_write_zeroes(s->qdev.conf.bs,
|
||||
r->req.cmd.lba * (s->qdev.blocksize / 512),
|
||||
nb_sectors * (s->qdev.blocksize / 512),
|
||||
flags, scsi_aio_complete, r);
|
||||
return;
|
||||
}
|
||||
|
||||
data = g_new0(WriteSameCBData, 1);
|
||||
data->r = r;
|
||||
data->sector = r->req.cmd.lba * (s->qdev.blocksize / 512);
|
||||
data->nb_sectors = nb_sectors * (s->qdev.blocksize / 512);
|
||||
data->iov.iov_len = MIN(data->nb_sectors * 512, SCSI_WRITE_SAME_MAX);
|
||||
data->iov.iov_base = buf = qemu_blockalign(s->qdev.conf.bs, data->iov.iov_len);
|
||||
qemu_iovec_init_external(&data->qiov, &data->iov, 1);
|
||||
|
||||
for (i = 0; i < data->iov.iov_len; i += s->qdev.blocksize) {
|
||||
memcpy(&buf[i], inbuf, s->qdev.blocksize);
|
||||
}
|
||||
|
||||
scsi_req_ref(&r->req);
|
||||
bdrv_acct_start(s->qdev.conf.bs, &r->acct, data->iov.iov_len, BDRV_ACCT_WRITE);
|
||||
r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, data->sector,
|
||||
&data->qiov, data->iov.iov_len / 512,
|
||||
scsi_write_same_complete, data);
|
||||
}
|
||||
|
||||
static void scsi_disk_emulate_write_data(SCSIRequest *req)
|
||||
@ -1597,6 +1720,10 @@ static void scsi_disk_emulate_write_data(SCSIRequest *req)
|
||||
scsi_disk_emulate_unmap(r, r->iov.iov_base);
|
||||
break;
|
||||
|
||||
case WRITE_SAME_10:
|
||||
case WRITE_SAME_16:
|
||||
scsi_disk_emulate_write_same(r, r->iov.iov_base);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
@ -1839,29 +1966,10 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
|
||||
break;
|
||||
case WRITE_SAME_10:
|
||||
case WRITE_SAME_16:
|
||||
nb_sectors = scsi_data_cdb_length(r->req.cmd.buf);
|
||||
if (bdrv_is_read_only(s->qdev.conf.bs)) {
|
||||
scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
|
||||
return 0;
|
||||
}
|
||||
if (!check_lba_range(s, r->req.cmd.lba, nb_sectors)) {
|
||||
goto illegal_lba;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only support WRITE SAME with the unmap bit set for now.
|
||||
*/
|
||||
if (!(req->cmd.buf[1] & 0x8)) {
|
||||
goto illegal_request;
|
||||
}
|
||||
|
||||
/* The request is used as the AIO opaque value, so add a ref. */
|
||||
scsi_req_ref(&r->req);
|
||||
r->req.aiocb = bdrv_aio_discard(s->qdev.conf.bs,
|
||||
r->req.cmd.lba * (s->qdev.blocksize / 512),
|
||||
nb_sectors * (s->qdev.blocksize / 512),
|
||||
scsi_aio_complete, r);
|
||||
return 0;
|
||||
DPRINTF("WRITE SAME %d (len %lu)\n",
|
||||
req->cmd.buf[0] == WRITE_SAME_10 ? 10 : 16,
|
||||
(long)r->req.cmd.xfer);
|
||||
break;
|
||||
default:
|
||||
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
|
||||
scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
|
||||
|
@ -216,6 +216,9 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors);
|
||||
int bdrv_write_zeroes(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, BdrvRequestFlags flags);
|
||||
BlockDriverAIOCB *bdrv_aio_write_zeroes(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, BdrvRequestFlags flags,
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags);
|
||||
int bdrv_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov);
|
||||
int bdrv_pread(BlockDriverState *bs, int64_t offset,
|
||||
@ -311,6 +314,7 @@ typedef struct BlockRequest {
|
||||
/* Fields to be filled by multiwrite caller */
|
||||
int64_t sector;
|
||||
int nb_sectors;
|
||||
int flags;
|
||||
QEMUIOVector *qiov;
|
||||
BlockDriverCompletionFunc *cb;
|
||||
void *opaque;
|
||||
|
@ -176,7 +176,9 @@ struct BlockDriver {
|
||||
int (*bdrv_snapshot_list)(BlockDriverState *bs,
|
||||
QEMUSnapshotInfo **psn_info);
|
||||
int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs,
|
||||
const char *snapshot_name);
|
||||
const char *snapshot_id,
|
||||
const char *name,
|
||||
Error **errp);
|
||||
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
|
||||
ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs);
|
||||
|
||||
@ -245,6 +247,9 @@ typedef struct BlockLimits {
|
||||
|
||||
/* optimal alignment for write zeroes requests in sectors */
|
||||
int64_t write_zeroes_alignment;
|
||||
|
||||
/* optimal transfer length in sectors */
|
||||
int opt_transfer_length;
|
||||
} BlockLimits;
|
||||
|
||||
/*
|
||||
|
@ -105,7 +105,6 @@ bool qemu_in_coroutine(void);
|
||||
*/
|
||||
typedef struct CoQueue {
|
||||
QTAILQ_HEAD(, Coroutine) entries;
|
||||
AioContext *ctx;
|
||||
} CoQueue;
|
||||
|
||||
/**
|
||||
@ -120,12 +119,6 @@ void qemu_co_queue_init(CoQueue *queue);
|
||||
*/
|
||||
void coroutine_fn qemu_co_queue_wait(CoQueue *queue);
|
||||
|
||||
/**
|
||||
* Adds the current coroutine to the head of the CoQueue and transfers control to the
|
||||
* caller of the coroutine.
|
||||
*/
|
||||
void coroutine_fn qemu_co_queue_wait_insert_head(CoQueue *queue);
|
||||
|
||||
/**
|
||||
* Restarts the next coroutine in the CoQueue and removes it from the queue.
|
||||
*
|
||||
|
@ -27,6 +27,14 @@
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/option.h"
|
||||
|
||||
|
||||
#define SNAPSHOT_OPT_BASE "snapshot."
|
||||
#define SNAPSHOT_OPT_ID "snapshot.id"
|
||||
#define SNAPSHOT_OPT_NAME "snapshot.name"
|
||||
|
||||
extern QemuOptsList internal_snapshot_opts;
|
||||
|
||||
typedef struct QEMUSnapshotInfo {
|
||||
char id_str[128]; /* unique snapshot id */
|
||||
@ -61,5 +69,10 @@ void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
|
||||
int bdrv_snapshot_list(BlockDriverState *bs,
|
||||
QEMUSnapshotInfo **psn_info);
|
||||
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
|
||||
const char *snapshot_name);
|
||||
const char *snapshot_id,
|
||||
const char *name,
|
||||
Error **errp);
|
||||
int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs,
|
||||
const char *id_or_name,
|
||||
Error **errp);
|
||||
#endif
|
||||
|
@ -41,14 +41,6 @@ void coroutine_fn qemu_co_queue_wait(CoQueue *queue)
|
||||
assert(qemu_in_coroutine());
|
||||
}
|
||||
|
||||
void coroutine_fn qemu_co_queue_wait_insert_head(CoQueue *queue)
|
||||
{
|
||||
Coroutine *self = qemu_coroutine_self();
|
||||
QTAILQ_INSERT_HEAD(&queue->entries, self, co_queue_next);
|
||||
qemu_coroutine_yield();
|
||||
assert(qemu_in_coroutine());
|
||||
}
|
||||
|
||||
/**
|
||||
* qemu_co_queue_run_restart:
|
||||
*
|
||||
|
@ -34,9 +34,9 @@ STEXI
|
||||
ETEXI
|
||||
|
||||
DEF("convert", img_convert,
|
||||
"convert [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] [-S sparse_size] filename [filename2 [...]] output_filename")
|
||||
"convert [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] filename [filename2 [...]] output_filename")
|
||||
STEXI
|
||||
@item convert [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||
@item convert [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||
ETEXI
|
||||
|
||||
DEF("info", img_info,
|
||||
|
169
qemu-img.c
169
qemu-img.c
@ -93,6 +93,11 @@ static void help(void)
|
||||
" 'options' is a comma separated list of format specific options in a\n"
|
||||
" name=value format. Use -o ? for an overview of the options supported by the\n"
|
||||
" used format\n"
|
||||
" 'snapshot_param' is param used for internal snapshot, format\n"
|
||||
" is 'snapshot.id=[ID],snapshot.name=[NAME]', or\n"
|
||||
" '[ID_OR_NAME]'\n"
|
||||
" 'snapshot_id_or_name' is deprecated, use 'snapshot_param'\n"
|
||||
" instead\n"
|
||||
" '-c' indicates that target image must be compressed (qcow format only)\n"
|
||||
" '-u' enables unsafe rebasing. It is assumed that old and new backing file\n"
|
||||
" match exactly. The image doesn't need a working backing file before\n"
|
||||
@ -105,7 +110,6 @@ static void help(void)
|
||||
" conversion. If the number of bytes is 0, the source will not be scanned for\n"
|
||||
" unallocated or zero sectors, and the destination image will always be\n"
|
||||
" fully allocated\n"
|
||||
" images will always be fully allocated\n"
|
||||
" '--output' takes the format in which the output must be done (human or json)\n"
|
||||
" '-n' skips the target volume creation (useful if the volume is created\n"
|
||||
" prior to running qemu-img)\n"
|
||||
@ -1125,25 +1129,27 @@ out3:
|
||||
|
||||
static int img_convert(int argc, char **argv)
|
||||
{
|
||||
int c, ret = 0, n, n1, bs_n, bs_i, compress, cluster_size,
|
||||
cluster_sectors, skip_create;
|
||||
int c, n, n1, bs_n, bs_i, compress, cluster_sectors, skip_create;
|
||||
int64_t ret = 0;
|
||||
int progress = 0, flags;
|
||||
const char *fmt, *out_fmt, *cache, *out_baseimg, *out_filename;
|
||||
BlockDriver *drv, *proto_drv;
|
||||
BlockDriverState **bs = NULL, *out_bs = NULL;
|
||||
int64_t total_sectors, nb_sectors, sector_num, bs_offset;
|
||||
int64_t total_sectors, nb_sectors, sector_num, bs_offset,
|
||||
sector_num_next_status = 0;
|
||||
uint64_t bs_sectors;
|
||||
uint8_t * buf = NULL;
|
||||
size_t bufsectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE;
|
||||
const uint8_t *buf1;
|
||||
BlockDriverInfo bdi;
|
||||
QEMUOptionParameter *param = NULL, *create_options = NULL;
|
||||
QEMUOptionParameter *out_baseimg_param;
|
||||
char *options = NULL;
|
||||
const char *snapshot_name = NULL;
|
||||
float local_progress = 0;
|
||||
int min_sparse = 8; /* Need at least 4k of zeros for sparse detection */
|
||||
bool quiet = false;
|
||||
Error *local_err = NULL;
|
||||
QemuOpts *sn_opts = NULL;
|
||||
|
||||
fmt = NULL;
|
||||
out_fmt = "raw";
|
||||
@ -1152,7 +1158,7 @@ static int img_convert(int argc, char **argv)
|
||||
compress = 0;
|
||||
skip_create = 0;
|
||||
for(;;) {
|
||||
c = getopt(argc, argv, "f:O:B:s:hce6o:pS:t:qn");
|
||||
c = getopt(argc, argv, "f:O:B:s:hce6o:pS:t:qnl:");
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
@ -1187,6 +1193,18 @@ static int img_convert(int argc, char **argv)
|
||||
case 's':
|
||||
snapshot_name = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) {
|
||||
sn_opts = qemu_opts_parse(&internal_snapshot_opts, optarg, 0);
|
||||
if (!sn_opts) {
|
||||
error_report("Failed in parsing snapshot param '%s'",
|
||||
optarg);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
snapshot_name = optarg;
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
{
|
||||
int64_t sval;
|
||||
@ -1227,7 +1245,7 @@ static int img_convert(int argc, char **argv)
|
||||
out_filename = argv[argc - 1];
|
||||
|
||||
/* Initialize before goto out */
|
||||
qemu_progress_init(progress, 2.0);
|
||||
qemu_progress_init(progress, 1.0);
|
||||
|
||||
if (options && is_help_option(options)) {
|
||||
ret = print_block_option_help(out_filename, out_fmt);
|
||||
@ -1258,17 +1276,26 @@ static int img_convert(int argc, char **argv)
|
||||
total_sectors += bs_sectors;
|
||||
}
|
||||
|
||||
if (snapshot_name != NULL) {
|
||||
if (sn_opts) {
|
||||
ret = bdrv_snapshot_load_tmp(bs[0],
|
||||
qemu_opt_get(sn_opts, SNAPSHOT_OPT_ID),
|
||||
qemu_opt_get(sn_opts, SNAPSHOT_OPT_NAME),
|
||||
&local_err);
|
||||
} else if (snapshot_name != NULL) {
|
||||
if (bs_n > 1) {
|
||||
error_report("No support for concatenating multiple snapshot");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
if (bdrv_snapshot_load_tmp(bs[0], snapshot_name) < 0) {
|
||||
error_report("Failed to load snapshot");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bdrv_snapshot_load_tmp_by_id_or_name(bs[0], snapshot_name, &local_err);
|
||||
}
|
||||
if (error_is_set(&local_err)) {
|
||||
error_report("Failed to load snapshot: %s",
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Find driver and parse its options */
|
||||
@ -1371,7 +1398,16 @@ static int img_convert(int argc, char **argv)
|
||||
bs_i = 0;
|
||||
bs_offset = 0;
|
||||
bdrv_get_geometry(bs[0], &bs_sectors);
|
||||
buf = qemu_blockalign(out_bs, IO_BUF_SIZE);
|
||||
|
||||
/* increase bufsectors from the default 4096 (2M) if opt_transfer_length
|
||||
* or discard_alignment of the out_bs is greater. Limit to 32768 (16MB)
|
||||
* as maximum. */
|
||||
bufsectors = MIN(32768,
|
||||
MAX(bufsectors, MAX(out_bs->bl.opt_transfer_length,
|
||||
out_bs->bl.discard_alignment))
|
||||
);
|
||||
|
||||
buf = qemu_blockalign(out_bs, bufsectors * BDRV_SECTOR_SIZE);
|
||||
|
||||
if (skip_create) {
|
||||
int64_t output_length = bdrv_getlength(out_bs);
|
||||
@ -1387,26 +1423,26 @@ static int img_convert(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (compress) {
|
||||
ret = bdrv_get_info(out_bs, &bdi);
|
||||
if (ret < 0) {
|
||||
cluster_sectors = 0;
|
||||
ret = bdrv_get_info(out_bs, &bdi);
|
||||
if (ret < 0) {
|
||||
if (compress) {
|
||||
error_report("could not get block driver info");
|
||||
goto out;
|
||||
}
|
||||
cluster_size = bdi.cluster_size;
|
||||
if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE) {
|
||||
} else {
|
||||
cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
if (compress) {
|
||||
if (cluster_sectors <= 0 || cluster_sectors > bufsectors) {
|
||||
error_report("invalid cluster size");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
cluster_sectors = cluster_size >> 9;
|
||||
sector_num = 0;
|
||||
|
||||
nb_sectors = total_sectors;
|
||||
if (nb_sectors != 0) {
|
||||
local_progress = (float)100 /
|
||||
(nb_sectors / MIN(nb_sectors, cluster_sectors));
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
int64_t bs_num;
|
||||
@ -1464,7 +1500,7 @@ static int img_convert(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
sector_num += n;
|
||||
qemu_progress_print(local_progress, 100);
|
||||
qemu_progress_print(100.0 * sector_num / total_sectors, 0);
|
||||
}
|
||||
/* signal EOF to align */
|
||||
bdrv_write_compressed(out_bs, 0, NULL, 0);
|
||||
@ -1481,21 +1517,13 @@ static int img_convert(int argc, char **argv)
|
||||
|
||||
sector_num = 0; // total number of sectors converted so far
|
||||
nb_sectors = total_sectors - sector_num;
|
||||
if (nb_sectors != 0) {
|
||||
local_progress = (float)100 /
|
||||
(nb_sectors / MIN(nb_sectors, IO_BUF_SIZE / 512));
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
nb_sectors = total_sectors - sector_num;
|
||||
if (nb_sectors <= 0) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
if (nb_sectors >= (IO_BUF_SIZE / 512)) {
|
||||
n = (IO_BUF_SIZE / 512);
|
||||
} else {
|
||||
n = nb_sectors;
|
||||
}
|
||||
|
||||
while (sector_num - bs_offset >= bs_sectors) {
|
||||
bs_i ++;
|
||||
@ -1507,34 +1535,59 @@ static int img_convert(int argc, char **argv)
|
||||
sector_num, bs_i, bs_offset, bs_sectors); */
|
||||
}
|
||||
|
||||
if (n > bs_offset + bs_sectors - sector_num) {
|
||||
n = bs_offset + bs_sectors - sector_num;
|
||||
}
|
||||
|
||||
/* If the output image is being created as a copy on write image,
|
||||
assume that sectors which are unallocated in the input image
|
||||
are present in both the output's and input's base images (no
|
||||
need to copy them). */
|
||||
if (out_baseimg) {
|
||||
ret = bdrv_is_allocated(bs[bs_i], sector_num - bs_offset,
|
||||
n, &n1);
|
||||
if ((out_baseimg || has_zero_init) &&
|
||||
sector_num >= sector_num_next_status) {
|
||||
n = nb_sectors > INT_MAX ? INT_MAX : nb_sectors;
|
||||
ret = bdrv_get_block_status(bs[bs_i], sector_num - bs_offset,
|
||||
n, &n1);
|
||||
if (ret < 0) {
|
||||
error_report("error while reading metadata for sector "
|
||||
"%" PRId64 ": %s",
|
||||
sector_num - bs_offset, strerror(-ret));
|
||||
error_report("error while reading block status of sector %"
|
||||
PRId64 ": %s", sector_num - bs_offset,
|
||||
strerror(-ret));
|
||||
goto out;
|
||||
}
|
||||
if (!ret) {
|
||||
/* If the output image is zero initialized, we are not working
|
||||
* on a shared base and the input is zero we can skip the next
|
||||
* n1 sectors */
|
||||
if (has_zero_init && !out_baseimg && (ret & BDRV_BLOCK_ZERO)) {
|
||||
sector_num += n1;
|
||||
continue;
|
||||
}
|
||||
/* The next 'n1' sectors are allocated in the input image. Copy
|
||||
only those as they may be followed by unallocated sectors. */
|
||||
n = n1;
|
||||
} else {
|
||||
n1 = n;
|
||||
/* If the output image is being created as a copy on write
|
||||
* image, assume that sectors which are unallocated in the
|
||||
* input image are present in both the output's and input's
|
||||
* base images (no need to copy them). */
|
||||
if (out_baseimg) {
|
||||
if (!(ret & BDRV_BLOCK_DATA)) {
|
||||
sector_num += n1;
|
||||
continue;
|
||||
}
|
||||
/* The next 'n1' sectors are allocated in the input image.
|
||||
* Copy only those as they may be followed by unallocated
|
||||
* sectors. */
|
||||
nb_sectors = n1;
|
||||
}
|
||||
/* avoid redundant callouts to get_block_status */
|
||||
sector_num_next_status = sector_num + n1;
|
||||
}
|
||||
|
||||
n = MIN(nb_sectors, bufsectors);
|
||||
|
||||
/* round down request length to an aligned sector, but
|
||||
* do not bother doing this on short requests. They happen
|
||||
* when we found an all-zero area, and the next sector to
|
||||
* write will not be sector_num + n. */
|
||||
if (cluster_sectors > 0 && n >= cluster_sectors) {
|
||||
int64_t next_aligned_sector = (sector_num + n);
|
||||
next_aligned_sector -= next_aligned_sector % cluster_sectors;
|
||||
if (sector_num + n > next_aligned_sector) {
|
||||
n = next_aligned_sector - sector_num;
|
||||
}
|
||||
}
|
||||
|
||||
n = MIN(n, bs_sectors - (sector_num - bs_offset));
|
||||
n1 = n;
|
||||
|
||||
ret = bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n);
|
||||
if (ret < 0) {
|
||||
error_report("error while reading sector %" PRId64 ": %s",
|
||||
@ -1559,14 +1612,20 @@ static int img_convert(int argc, char **argv)
|
||||
n -= n1;
|
||||
buf1 += n1 * 512;
|
||||
}
|
||||
qemu_progress_print(local_progress, 100);
|
||||
qemu_progress_print(100.0 * sector_num / total_sectors, 0);
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (!ret) {
|
||||
qemu_progress_print(100, 0);
|
||||
}
|
||||
qemu_progress_end();
|
||||
free_option_parameters(create_options);
|
||||
free_option_parameters(param);
|
||||
qemu_vfree(buf);
|
||||
if (sn_opts) {
|
||||
qemu_opts_del(sn_opts);
|
||||
}
|
||||
if (out_bs) {
|
||||
bdrv_unref(out_bs);
|
||||
}
|
||||
|
@ -46,7 +46,11 @@ is the destination disk image filename
|
||||
is a comma separated list of format specific options in a
|
||||
name=value format. Use @code{-o ?} for an overview of the options supported
|
||||
by the used format or see the format descriptions below for details.
|
||||
|
||||
@item snapshot_param
|
||||
is param used for internal snapshot, format is
|
||||
'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]'
|
||||
@item snapshot_id_or_name
|
||||
is deprecated, use snapshot_param instead
|
||||
|
||||
@item -c
|
||||
indicates that target image must be compressed (qcow format only)
|
||||
@ -179,10 +183,10 @@ Error on reading data
|
||||
|
||||
@end table
|
||||
|
||||
@item convert [-c] [-p] [-n] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||
@item convert [-c] [-p] [-n] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||
|
||||
Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename}
|
||||
using format @var{output_fmt}. It can be optionally compressed (@code{-c}
|
||||
Convert the disk image @var{filename} or a snapshot @var{snapshot_param}(@var{snapshot_id_or_name} is deprecated)
|
||||
to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c}
|
||||
option) or use any format specific options like encryption (@code{-o} option).
|
||||
|
||||
Only the formats @code{qcow} and @code{qcow2} support compression. The
|
||||
|
47
qemu-nbd.c
47
qemu-nbd.c
@ -20,6 +20,7 @@
|
||||
#include "block/block.h"
|
||||
#include "block/nbd.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "block/snapshot.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
@ -78,8 +79,16 @@ static void usage(const char *name)
|
||||
#endif
|
||||
"\n"
|
||||
"Block device options:\n"
|
||||
" -f, --format=FORMAT set image format (raw, qcow2, ...)\n"
|
||||
" -r, --read-only export read-only\n"
|
||||
" -s, --snapshot use snapshot file\n"
|
||||
" -s, --snapshot use FILE as an external snapshot, create a temporary\n"
|
||||
" file with backing_file=FILE, redirect the write to\n"
|
||||
" the temporary one\n"
|
||||
" -l, --load-snapshot=SNAPSHOT_PARAM\n"
|
||||
" load an internal snapshot inside FILE and export it\n"
|
||||
" as an read-only device, SNAPSHOT_PARAM format is\n"
|
||||
" 'snapshot.id=[ID],snapshot.name=[NAME]', or\n"
|
||||
" '[ID_OR_NAME]'\n"
|
||||
" -n, --nocache disable host cache\n"
|
||||
" --cache=MODE set cache mode (none, writeback, ...)\n"
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
@ -315,7 +324,9 @@ int main(int argc, char **argv)
|
||||
char *device = NULL;
|
||||
int port = NBD_DEFAULT_PORT;
|
||||
off_t fd_size;
|
||||
const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:t";
|
||||
QemuOpts *sn_opts = NULL;
|
||||
const char *sn_id_or_name = NULL;
|
||||
const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:";
|
||||
struct option lopt[] = {
|
||||
{ "help", 0, NULL, 'h' },
|
||||
{ "version", 0, NULL, 'V' },
|
||||
@ -328,6 +339,7 @@ int main(int argc, char **argv)
|
||||
{ "connect", 1, NULL, 'c' },
|
||||
{ "disconnect", 0, NULL, 'd' },
|
||||
{ "snapshot", 0, NULL, 's' },
|
||||
{ "load-snapshot", 1, NULL, 'l' },
|
||||
{ "nocache", 0, NULL, 'n' },
|
||||
{ "cache", 1, NULL, QEMU_NBD_OPT_CACHE },
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
@ -428,6 +440,17 @@ int main(int argc, char **argv)
|
||||
errx(EXIT_FAILURE, "Offset must be positive `%s'", optarg);
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) {
|
||||
sn_opts = qemu_opts_parse(&internal_snapshot_opts, optarg, 0);
|
||||
if (!sn_opts) {
|
||||
errx(EXIT_FAILURE, "Failed in parsing snapshot param `%s'",
|
||||
optarg);
|
||||
}
|
||||
} else {
|
||||
sn_id_or_name = optarg;
|
||||
}
|
||||
/* fall through */
|
||||
case 'r':
|
||||
nbdflags |= NBD_FLAG_READ_ONLY;
|
||||
flags &= ~BDRV_O_RDWR;
|
||||
@ -581,6 +604,22 @@ int main(int argc, char **argv)
|
||||
error_get_pretty(local_err));
|
||||
}
|
||||
|
||||
if (sn_opts) {
|
||||
ret = bdrv_snapshot_load_tmp(bs,
|
||||
qemu_opt_get(sn_opts, SNAPSHOT_OPT_ID),
|
||||
qemu_opt_get(sn_opts, SNAPSHOT_OPT_NAME),
|
||||
&local_err);
|
||||
} else if (sn_id_or_name) {
|
||||
ret = bdrv_snapshot_load_tmp_by_id_or_name(bs, sn_id_or_name,
|
||||
&local_err);
|
||||
}
|
||||
if (ret < 0) {
|
||||
errno = -ret;
|
||||
err(EXIT_FAILURE,
|
||||
"Failed to load snapshot: %s",
|
||||
error_get_pretty(local_err));
|
||||
}
|
||||
|
||||
fd_size = bdrv_getlength(bs);
|
||||
|
||||
if (partition != -1) {
|
||||
@ -641,6 +680,10 @@ int main(int argc, char **argv)
|
||||
unlink(sockpath);
|
||||
}
|
||||
|
||||
if (sn_opts) {
|
||||
qemu_opts_del(sn_opts);
|
||||
}
|
||||
|
||||
if (device) {
|
||||
void *ret;
|
||||
pthread_join(client_thread, &ret);
|
||||
|
@ -22,12 +22,20 @@ Export QEMU disk image using NBD protocol.
|
||||
interface to bind to (default @samp{0.0.0.0})
|
||||
@item -k, --socket=@var{path}
|
||||
Use a unix socket with path @var{path}
|
||||
@item -f, --format=@var{format}
|
||||
Set image format as @var{format}
|
||||
@item -r, --read-only
|
||||
export read-only
|
||||
@item -P, --partition=@var{num}
|
||||
only expose partition @var{num}
|
||||
@item -s, --snapshot
|
||||
use snapshot file
|
||||
use @var{filename} as an external snapshot, create a temporary
|
||||
file with backing_file=@var{filename}, redirect the write to
|
||||
the temporary one
|
||||
@item -l, --load-snapshot=@var{snapshot_param}
|
||||
load an internal snapshot inside @var{filename} and export it
|
||||
as an read-only device, @var{snapshot_param} format is
|
||||
'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]'
|
||||
@item -n, --nocache
|
||||
@itemx --cache=@var{cache}
|
||||
set cache mode to be used with the file. See the documentation of
|
||||
|
@ -44,7 +44,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
_supported_fmt qcow2
|
||||
_supported_proto generic
|
||||
_supported_os Linux
|
||||
|
||||
_default_cache_mode "writethrough"
|
||||
_supported_cache_modes "writethrough" "none"
|
||||
|
||||
echo "Errors while writing 128 kB"
|
||||
echo
|
||||
|
@ -44,7 +44,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
_supported_fmt qcow2
|
||||
_supported_proto generic
|
||||
_supported_os Linux
|
||||
_unsupported_qemu_io_options --nocache
|
||||
_default_cache_mode "writethrough"
|
||||
_supported_cache_modes "writethrough"
|
||||
|
||||
size=128M
|
||||
|
||||
|
@ -81,32 +81,5 @@ cp "$TEST_IMG" "$TEST_IMG2"
|
||||
io_pattern write 512 512 0 1 101
|
||||
_compare
|
||||
|
||||
# Test cluster allocated in one, with IO error
|
||||
cat > "$TEST_DIR/blkdebug.conf"<<EOF
|
||||
[inject-error]
|
||||
event = "read_aio"
|
||||
errno = "5"
|
||||
once ="off"
|
||||
EOF
|
||||
_make_test_img $size
|
||||
cp "$TEST_IMG" "$TEST_IMG2"
|
||||
io_pattern write 512 512 0 1 102
|
||||
TEST_IMG="blkdebug:$TEST_DIR/blkdebug.conf:$TEST_IMG" _compare 2>&1 |\
|
||||
_filter_testdir | _filter_imgfmt
|
||||
|
||||
# Test cluster allocated in one, with different sizes and IO error in the part
|
||||
# that exists only in one image
|
||||
cat > "$TEST_DIR/blkdebug.conf"<<EOF
|
||||
[inject-error]
|
||||
event = "read_aio"
|
||||
errno = "5"
|
||||
once ="off"
|
||||
EOF
|
||||
_make_test_img $size
|
||||
TEST_IMG="$TEST_IMG2" _make_test_img 0
|
||||
io_pattern write 512 512 0 1 102
|
||||
TEST_IMG="blkdebug:$TEST_DIR/blkdebug.conf:$TEST_IMG" _compare 2>&1 |\
|
||||
_filter_testdir | _filter_imgfmt
|
||||
|
||||
# Cleanup
|
||||
status=0
|
||||
|
@ -37,20 +37,4 @@ wrote 512/512 bytes at offset 512
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
Content mismatch at offset 512!
|
||||
1
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||
=== IO: pattern 102
|
||||
wrote 512/512 bytes at offset 512
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-img: Error while reading offset 0 of blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
|
||||
qemu-img: Error while reading offset 0: Input/output error
|
||||
4
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||
Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=0
|
||||
=== IO: pattern 102
|
||||
wrote 512/512 bytes at offset 512
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-img: Error while reading offset 0 of blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
|
||||
qemu-img: Error while reading offset 0 of blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
|
||||
Warning: Image size mismatch!
|
||||
4
|
||||
Cleanup
|
||||
|
@ -39,12 +39,12 @@ ide0-hd0: TEST_DIR/t.qcow2 (qcow2)
|
||||
=== Enable and disable lazy refcounting on the command line, plus some invalid values ===
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off'
|
||||
@ -63,71 +63,71 @@ Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: could not open disk image TEST_DIR/t.qcow2: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
|
||||
=== No medium ===
|
||||
|
||||
Testing: -drive if=floppy
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive if=ide,media=cdrom
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive if=scsi,media=cdrom
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive if=ide
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) QEMU_PROG: Device needs media, but drive is empty
|
||||
QEMU_PROG: Device initialization failed.
|
||||
QEMU_PROG: Initialization of device ide-hd failed
|
||||
|
||||
Testing: -drive if=virtio
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) QEMU_PROG: -drive if=virtio: Device needs media, but drive is empty
|
||||
QEMU_PROG: -drive if=virtio: Device initialization failed.
|
||||
QEMU_PROG: -drive if=virtio: Device initialization failed.
|
||||
QEMU_PROG: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized
|
||||
|
||||
Testing: -drive if=scsi
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) QEMU_PROG: -drive if=scsi: Device needs media, but drive is empty
|
||||
QEMU_PROG: Device initialization failed.
|
||||
QEMU_PROG: Initialization of device lsi53c895a failed
|
||||
|
||||
Testing: -drive if=none,id=disk -device ide-cd,drive=disk
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive if=none,id=disk -device ide-drive,drive=disk
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) QEMU_PROG: -device ide-drive,drive=disk: Device needs media, but drive is empty
|
||||
QEMU_PROG: -device ide-drive,drive=disk: Device initialization failed.
|
||||
QEMU_PROG: -device ide-drive,drive=disk: Device 'ide-drive' could not be initialized
|
||||
|
||||
Testing: -drive if=none,id=disk -device ide-hd,drive=disk
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) QEMU_PROG: -device ide-hd,drive=disk: Device needs media, but drive is empty
|
||||
QEMU_PROG: -device ide-hd,drive=disk: Device initialization failed.
|
||||
QEMU_PROG: -device ide-hd,drive=disk: Device 'ide-hd' could not be initialized
|
||||
|
||||
Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-disk,drive=disk
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) QEMU_PROG: -device scsi-disk,drive=disk: Device needs media, but drive is empty
|
||||
QEMU_PROG: -device scsi-disk,drive=disk: Device initialization failed.
|
||||
QEMU_PROG: -device scsi-disk,drive=disk: Device 'scsi-disk' could not be initialized
|
||||
|
||||
Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-hd,drive=disk
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) QEMU_PROG: -device scsi-hd,drive=disk: Device needs media, but drive is empty
|
||||
QEMU_PROG: -device scsi-hd,drive=disk: Device initialization failed.
|
||||
QEMU_PROG: -device scsi-hd,drive=disk: Device 'scsi-hd' could not be initialized
|
||||
@ -136,81 +136,81 @@ QEMU_PROG: -device scsi-hd,drive=disk: Device 'scsi-hd' could not be initialized
|
||||
=== Read-only ===
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=floppy,readonly=on
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=ide,media=cdrom,readonly=on
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,media=cdrom,readonly=on
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) QEMU_PROG: Can't use a read-only drive
|
||||
QEMU_PROG: Device initialization failed.
|
||||
QEMU_PROG: Initialization of device ide-hd failed
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,readonly=on
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-cd,drive=disk
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-drive,drive=disk
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) QEMU_PROG: -device ide-drive,drive=disk: Can't use a read-only drive
|
||||
QEMU_PROG: -device ide-drive,drive=disk: Device initialization failed.
|
||||
QEMU_PROG: -device ide-drive,drive=disk: Device 'ide-drive' could not be initialized
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-hd,drive=disk
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) QEMU_PROG: -device ide-hd,drive=disk: Can't use a read-only drive
|
||||
QEMU_PROG: -device ide-hd,drive=disk: Device initialization failed.
|
||||
QEMU_PROG: -device ide-hd,drive=disk: Device 'ide-hd' could not be initialized
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
|
||||
=== Cache modes ===
|
||||
|
||||
Testing: -drive media=cdrom,cache=none
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive media=cdrom,cache=directsync
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive media=cdrom,cache=writeback
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive media=cdrom,cache=writethrough
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive media=cdrom,cache=unsafe
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive media=cdrom,cache=invalid_value
|
||||
QEMU_PROG: -drive media=cdrom,cache=invalid_value: invalid cache option
|
||||
@ -219,8 +219,8 @@ QEMU_PROG: -drive media=cdrom,cache=invalid_value: invalid cache option
|
||||
=== Specifying the protocol layer ===
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,file.driver=file
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Can't use 'qcow2' as a block driver for the protocol level
|
||||
|
@ -41,8 +41,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
_supported_fmt generic
|
||||
_supported_proto generic
|
||||
_supported_os Linux
|
||||
_unsupported_qemu_io_options --nocache
|
||||
|
||||
_default_cache_mode "writethrough"
|
||||
_supported_cache_modes "writethrough"
|
||||
|
||||
size=128M
|
||||
_make_test_img $size
|
||||
|
138
tests/qemu-iotests/058
Executable file
138
tests/qemu-iotests/058
Executable file
@ -0,0 +1,138 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Test export internal snapshot by qemu-nbd, convert it by qemu-img.
|
||||
#
|
||||
# Copyright (C) 2013 IBM, Inc.
|
||||
#
|
||||
# Based on 029.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# creator
|
||||
owner=xiawenc@linux.vnet.ibm.com
|
||||
|
||||
seq=`basename $0`
|
||||
echo "QA output created by $seq"
|
||||
|
||||
here=`pwd`
|
||||
tmp=/tmp/$$
|
||||
status=1 # failure is the default!
|
||||
|
||||
nbd_unix_socket=$TEST_DIR/test_qemu_nbd_socket
|
||||
nbd_snapshot_img="nbd:unix:$nbd_unix_socket"
|
||||
|
||||
_cleanup_nbd()
|
||||
{
|
||||
if [ -n "$NBD_SNAPSHOT_PID" ]; then
|
||||
kill "$NBD_SNAPSHOT_PID"
|
||||
fi
|
||||
rm -f "$nbd_unix_socket"
|
||||
}
|
||||
|
||||
_wait_for_nbd()
|
||||
{
|
||||
for ((i = 0; i < 300; i++))
|
||||
do
|
||||
if [ -r "$nbd_unix_socket" ]; then
|
||||
return
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
echo "Failed in check of unix socket created by qemu-nbd"
|
||||
exit 1
|
||||
}
|
||||
|
||||
converted_image=$TEST_IMG.converted
|
||||
|
||||
_export_nbd_snapshot()
|
||||
{
|
||||
_cleanup_nbd
|
||||
$QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l $1 &
|
||||
NBD_SNAPSHOT_PID=$!
|
||||
_wait_for_nbd
|
||||
}
|
||||
|
||||
_export_nbd_snapshot1()
|
||||
{
|
||||
_cleanup_nbd
|
||||
$QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l snapshot.name=$1 &
|
||||
NBD_SNAPSHOT_PID=$!
|
||||
_wait_for_nbd
|
||||
}
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_nbd
|
||||
_cleanup_test_img
|
||||
rm -f "$converted_image"
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
. ./common.pattern
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_require_command QEMU_NBD
|
||||
|
||||
echo
|
||||
echo "== preparing image =="
|
||||
_make_test_img 64M
|
||||
$QEMU_IO -c 'write -P 0xa 0x1000 0x1000' "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c 'write -P 0xb 0x2000 0x1000' "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG snapshot -c sn1 "$TEST_IMG"
|
||||
$QEMU_IO -c 'write -P 0xc 0x1000 0x1000' "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c 'write -P 0xd 0x2000 0x1000' "$TEST_IMG" | _filter_qemu_io
|
||||
_check_test_img
|
||||
|
||||
echo
|
||||
echo "== verifying the image file with patterns =="
|
||||
$QEMU_IO -c 'read -P 0xc 0x1000 0x1000' "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c 'read -P 0xd 0x2000 0x1000' "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
_export_nbd_snapshot sn1
|
||||
|
||||
echo
|
||||
echo "== verifying the exported snapshot with patterns, method 1 =="
|
||||
$QEMU_IO -c 'read -P 0xa 0x1000 0x1000' "$nbd_snapshot_img" | _filter_qemu_io
|
||||
$QEMU_IO -c 'read -P 0xb 0x2000 0x1000' "$nbd_snapshot_img" | _filter_qemu_io
|
||||
|
||||
_export_nbd_snapshot1 sn1
|
||||
|
||||
echo
|
||||
echo "== verifying the exported snapshot with patterns, method 2 =="
|
||||
$QEMU_IO -c 'read -P 0xa 0x1000 0x1000' "$nbd_snapshot_img" | _filter_qemu_io
|
||||
$QEMU_IO -c 'read -P 0xb 0x2000 0x1000' "$nbd_snapshot_img" | _filter_qemu_io
|
||||
|
||||
$QEMU_IMG convert "$TEST_IMG" -l sn1 -O qcow2 "$converted_image"
|
||||
|
||||
echo
|
||||
echo "== verifying the converted snapshot with patterns, method 1 =="
|
||||
$QEMU_IO -c 'read -P 0xa 0x1000 0x1000' "$converted_image" | _filter_qemu_io
|
||||
$QEMU_IO -c 'read -P 0xb 0x2000 0x1000' "$converted_image" | _filter_qemu_io
|
||||
|
||||
$QEMU_IMG convert "$TEST_IMG" -l snapshot.name=sn1 -O qcow2 "$converted_image"
|
||||
|
||||
echo
|
||||
echo "== verifying the converted snapshot with patterns, method 2 =="
|
||||
$QEMU_IO -c 'read -P 0xa 0x1000 0x1000' "$converted_image" | _filter_qemu_io
|
||||
$QEMU_IO -c 'read -P 0xb 0x2000 0x1000' "$converted_image" | _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
status=0
|
44
tests/qemu-iotests/058.out
Normal file
44
tests/qemu-iotests/058.out
Normal file
@ -0,0 +1,44 @@
|
||||
QA output created by 058
|
||||
|
||||
== preparing image ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
wrote 4096/4096 bytes at offset 4096
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 4096/4096 bytes at offset 8192
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 4096/4096 bytes at offset 4096
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 4096/4096 bytes at offset 8192
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
No errors were found on the image.
|
||||
|
||||
== verifying the image file with patterns ==
|
||||
read 4096/4096 bytes at offset 4096
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 4096/4096 bytes at offset 8192
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== verifying the exported snapshot with patterns, method 1 ==
|
||||
read 4096/4096 bytes at offset 4096
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 4096/4096 bytes at offset 8192
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== verifying the exported snapshot with patterns, method 2 ==
|
||||
read 4096/4096 bytes at offset 4096
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 4096/4096 bytes at offset 8192
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== verifying the converted snapshot with patterns, method 1 ==
|
||||
read 4096/4096 bytes at offset 4096
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 4096/4096 bytes at offset 8192
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== verifying the converted snapshot with patterns, method 2 ==
|
||||
read 4096/4096 bytes at offset 4096
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 4096/4096 bytes at offset 8192
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
*** done
|
@ -75,6 +75,11 @@ echo
|
||||
echo "=== Testing monolithicFlat with zeroed_grain ==="
|
||||
IMGOPTS="subformat=monolithicFlat,zeroed_grain=on" _make_test_img 2G
|
||||
|
||||
echo
|
||||
echo "=== Testing big twoGbMaxExtentFlat ==="
|
||||
IMGOPTS="subformat=twoGbMaxExtentFlat" _make_test_img 1000G
|
||||
$QEMU_IMG info $TEST_IMG | _filter_testdir | sed -e 's/cid: [0-9]*/cid: XXXXXXXX/'
|
||||
|
||||
echo
|
||||
echo "=== Testing version 3 ==="
|
||||
_use_sample_img iotest-version3.vmdk.bz2
|
||||
|
File diff suppressed because it is too large
Load Diff
86
tests/qemu-iotests/074
Executable file
86
tests/qemu-iotests/074
Executable file
@ -0,0 +1,86 @@
|
||||
#!/bin/bash
|
||||
##
|
||||
## qemu-img compare test (qcow2 only ones)
|
||||
##
|
||||
##
|
||||
## Copyright (C) 2013 Red Hat, Inc.
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
#
|
||||
# creator
|
||||
owner=famz@redhat.com
|
||||
|
||||
seq=`basename $0`
|
||||
echo "QA output created by $seq"
|
||||
|
||||
status=1 # failure is the default!
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
echo "Cleanup"
|
||||
_cleanup_test_img
|
||||
rm "${TEST_IMG2}"
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
_compare()
|
||||
{
|
||||
$QEMU_IMG compare "$@" "$TEST_IMG" "${TEST_IMG2}"
|
||||
echo $?
|
||||
}
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
. ./common.pattern
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
# Setup test basic parameters
|
||||
TEST_IMG2=$TEST_IMG.2
|
||||
CLUSTER_SIZE=4096
|
||||
size=1024M
|
||||
|
||||
# Test cluster allocated in one, with IO error
|
||||
cat > "$TEST_DIR/blkdebug.conf"<<EOF
|
||||
[inject-error]
|
||||
event = "read_aio"
|
||||
errno = "5"
|
||||
once ="off"
|
||||
EOF
|
||||
_make_test_img $size
|
||||
cp "$TEST_IMG" "$TEST_IMG2"
|
||||
io_pattern write 512 512 0 1 102
|
||||
TEST_IMG="blkdebug:$TEST_DIR/blkdebug.conf:$TEST_IMG" _compare 2>&1 |\
|
||||
_filter_testdir | _filter_imgfmt
|
||||
|
||||
# Test cluster allocated in one, with different sizes and IO error in the part
|
||||
# that exists only in one image
|
||||
cat > "$TEST_DIR/blkdebug.conf"<<EOF
|
||||
[inject-error]
|
||||
event = "read_aio"
|
||||
errno = "5"
|
||||
once ="off"
|
||||
EOF
|
||||
_make_test_img $size
|
||||
TEST_IMG="$TEST_IMG2" _make_test_img 0
|
||||
io_pattern write 512 512 0 1 102
|
||||
TEST_IMG="blkdebug:$TEST_DIR/blkdebug.conf:$TEST_IMG" _compare 2>&1 |\
|
||||
_filter_testdir | _filter_imgfmt
|
||||
|
||||
# Cleanup
|
||||
status=0
|
18
tests/qemu-iotests/074.out
Normal file
18
tests/qemu-iotests/074.out
Normal file
@ -0,0 +1,18 @@
|
||||
QA output created by 074
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||
=== IO: pattern 102
|
||||
wrote 512/512 bytes at offset 512
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-img: Error while reading offset 0 of blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
|
||||
qemu-img: Error while reading offset 0: Input/output error
|
||||
4
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||
Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=0
|
||||
=== IO: pattern 102
|
||||
wrote 512/512 bytes at offset 512
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-img: Error while reading offset 0 of blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
|
||||
qemu-img: Error while reading offset 0 of blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
|
||||
Warning: Image size mismatch!
|
||||
4
|
||||
Cleanup
|
@ -161,6 +161,7 @@ cat <<EOF
|
||||
QEMU -- $QEMU
|
||||
QEMU_IMG -- $QEMU_IMG
|
||||
QEMU_IO -- $QEMU_IO
|
||||
QEMU_NBD -- $QEMU_NBD
|
||||
IMGFMT -- $FULL_IMGFMT_DETAILS
|
||||
IMGPROTO -- $FULL_IMGPROTO_DETAILS
|
||||
PLATFORM -- $FULL_HOST_DETAILS
|
||||
@ -242,7 +243,7 @@ do
|
||||
fi
|
||||
|
||||
reference=$seq.out
|
||||
if (echo $QEMU_IO_OPTIONS | grep -s -- '--nocache' > /dev/null); then
|
||||
if [ "$CACHEMODE" = "none" ]; then
|
||||
[ -f $seq.out.nocache ] && reference=$seq.out.nocache
|
||||
fi
|
||||
|
||||
|
@ -42,13 +42,16 @@ expunge=true
|
||||
have_test_arg=false
|
||||
randomize=false
|
||||
valgrind=false
|
||||
cachemode=false
|
||||
rm -f $tmp.list $tmp.tmp $tmp.sed
|
||||
|
||||
export IMGFMT=raw
|
||||
export IMGFMT_GENERIC=true
|
||||
export IMGPROTO=file
|
||||
export IMGOPTS=""
|
||||
export CACHEMODE="writeback"
|
||||
export QEMU_IO_OPTIONS=""
|
||||
export CACHEMODE_IS_DEFAULT=true
|
||||
|
||||
for r
|
||||
do
|
||||
@ -113,7 +116,12 @@ s/ .*//p
|
||||
IMGOPTS="$r"
|
||||
imgopts=false
|
||||
continue
|
||||
|
||||
elif $cachemode
|
||||
then
|
||||
CACHEMODE="$r"
|
||||
CACHEMODE_IS_DEFAULT=false
|
||||
cachemode=false
|
||||
continue
|
||||
fi
|
||||
|
||||
xpand=true
|
||||
@ -124,7 +132,7 @@ s/ .*//p
|
||||
echo "Usage: $0 [options] [testlist]"'
|
||||
|
||||
common options
|
||||
-v verbose
|
||||
-v verbose
|
||||
|
||||
check options
|
||||
-raw test raw (default)
|
||||
@ -140,19 +148,20 @@ check options
|
||||
-sheepdog test sheepdog
|
||||
-nbd test nbd
|
||||
-ssh test ssh
|
||||
-xdiff graphical mode diff
|
||||
-nocache use O_DIRECT on backing file
|
||||
-misalign misalign memory allocations
|
||||
-n show me, do not run tests
|
||||
-xdiff graphical mode diff
|
||||
-nocache use O_DIRECT on backing file
|
||||
-misalign misalign memory allocations
|
||||
-n show me, do not run tests
|
||||
-o options -o options to pass to qemu-img create/convert
|
||||
-T output timestamps
|
||||
-r randomize test order
|
||||
-T output timestamps
|
||||
-r randomize test order
|
||||
-c mode cache mode
|
||||
|
||||
testlist options
|
||||
-g group[,group...] include tests from these groups
|
||||
-x group[,group...] exclude tests from these groups
|
||||
NNN include test NNN
|
||||
NNN-NNN include test range (eg. 012-021)
|
||||
NNN-NNN include test range (eg. 012-021)
|
||||
'
|
||||
exit 0
|
||||
;;
|
||||
@ -219,7 +228,8 @@ testlist options
|
||||
xpand=false
|
||||
;;
|
||||
-nocache)
|
||||
QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --nocache"
|
||||
CACHEMODE="none"
|
||||
CACHEMODE_IS_DEFAULT=false
|
||||
xpand=false
|
||||
;;
|
||||
|
||||
@ -258,6 +268,10 @@ testlist options
|
||||
imgopts=true
|
||||
xpand=false
|
||||
;;
|
||||
-c)
|
||||
cachemode=true
|
||||
xpand=false
|
||||
;;
|
||||
-r) # randomize test order
|
||||
randomize=true
|
||||
xpand=false
|
||||
@ -334,6 +348,9 @@ BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \
|
||||
|
||||
done
|
||||
|
||||
# Set qemu-io cache mode with $CACHEMODE we have
|
||||
QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --cache $CACHEMODE"
|
||||
|
||||
# Set default options for qemu-img create -o if they were not specified
|
||||
_set_default_imgopts
|
||||
|
||||
|
@ -157,7 +157,8 @@ _filter_qemu_io()
|
||||
_filter_qemu()
|
||||
{
|
||||
sed -e "s#\\(^\\|(qemu) \\)$(basename $QEMU_PROG):#\1QEMU_PROG:#" \
|
||||
-e 's#^QEMU [0-9]\+\.[0-9]\+\.[0-9]\+ monitor#QEMU X.Y.Z monitor#'
|
||||
-e 's#^QEMU [0-9]\+\.[0-9]\+\.[0-9]\+ monitor#QEMU X.Y.Z monitor#' \
|
||||
-e $'s#\r##' # QEMU monitor uses \r\n line endings
|
||||
}
|
||||
|
||||
# replace problematic QMP output like timestamps
|
||||
|
@ -387,25 +387,31 @@ _supported_os()
|
||||
_notrun "not suitable for this OS: $HOSTOS"
|
||||
}
|
||||
|
||||
_unsupported_qemu_io_options()
|
||||
_supported_cache_modes()
|
||||
{
|
||||
for bad_opt
|
||||
do
|
||||
for opt in $QEMU_IO_OPTIONS
|
||||
do
|
||||
if [ "$bad_opt" = "$opt" ]
|
||||
then
|
||||
_notrun "not suitable for qemu-io option: $bad_opt"
|
||||
fi
|
||||
done
|
||||
for mode; do
|
||||
if [ "$mode" = "$CACHEMODE" ]; then
|
||||
return
|
||||
fi
|
||||
done
|
||||
_notrun "not suitable for cache mode: $CACHEMODE"
|
||||
}
|
||||
|
||||
_default_cache_mode()
|
||||
{
|
||||
if $CACHEMODE_IS_DEFAULT; then
|
||||
CACHEMODE="$1"
|
||||
QEMU_IO="$QEMU_IO --cache $1"
|
||||
return
|
||||
fi
|
||||
}
|
||||
|
||||
# this test requires that a specified command (executable) exists
|
||||
#
|
||||
_require_command()
|
||||
{
|
||||
[ -x "$1" ] || _notrun "$1 utility required, skipped this test"
|
||||
eval c=\$$1
|
||||
[ -x "$c" ] || _notrun "$1 utility required, skipped this test"
|
||||
}
|
||||
|
||||
_full_imgfmt_details()
|
||||
|
@ -39,7 +39,7 @@
|
||||
030 rw auto backing
|
||||
031 rw auto quick
|
||||
032 rw auto
|
||||
033 rw auto
|
||||
033 rw auto quick
|
||||
034 rw auto backing
|
||||
035 rw auto quick
|
||||
036 rw auto quick
|
||||
@ -64,6 +64,7 @@
|
||||
055 rw auto
|
||||
056 rw auto backing
|
||||
057 rw auto
|
||||
058 rw auto
|
||||
059 rw auto
|
||||
060 rw auto
|
||||
061 rw auto
|
||||
@ -77,3 +78,4 @@
|
||||
069 rw auto
|
||||
070 rw auto
|
||||
073 rw auto
|
||||
074 rw auto
|
||||
|
@ -37,6 +37,7 @@ qemu_args = os.environ.get('QEMU', 'qemu').strip().split(' ')
|
||||
imgfmt = os.environ.get('IMGFMT', 'raw')
|
||||
imgproto = os.environ.get('IMGPROTO', 'file')
|
||||
test_dir = os.environ.get('TEST_DIR', '/var/tmp')
|
||||
cachemode = os.environ.get('CACHEMODE')
|
||||
|
||||
socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper')
|
||||
|
||||
@ -96,7 +97,7 @@ class VM(object):
|
||||
'''Add a virtio-blk drive to the VM'''
|
||||
options = ['if=virtio',
|
||||
'format=%s' % imgfmt,
|
||||
'cache=none',
|
||||
'cache=%s' % cachemode,
|
||||
'file=%s' % path,
|
||||
'id=drive%d' % self._num_drives]
|
||||
if opts:
|
||||
|
@ -195,7 +195,6 @@ static void test_bh_delete_from_cb(void)
|
||||
g_assert(data1.bh == NULL);
|
||||
|
||||
g_assert(!aio_poll(ctx, false));
|
||||
g_assert(!aio_poll(ctx, true));
|
||||
}
|
||||
|
||||
static void test_bh_delete_from_cb_many(void)
|
||||
|
@ -60,11 +60,12 @@ bdrv_aio_discard(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs
|
||||
bdrv_aio_flush(void *bs, void *opaque) "bs %p opaque %p"
|
||||
bdrv_aio_readv(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p"
|
||||
bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p"
|
||||
bdrv_aio_write_zeroes(void *bs, int64_t sector_num, int nb_sectors, int flags, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d flags %#x opaque %p"
|
||||
bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
|
||||
bdrv_co_readv(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d"
|
||||
bdrv_co_copy_on_readv(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d"
|
||||
bdrv_co_writev(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d"
|
||||
bdrv_co_write_zeroes(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d"
|
||||
bdrv_co_write_zeroes(void *bs, int64_t sector_num, int nb_sector, int flags) "bs %p sector_num %"PRId64" nb_sectors %d flags %#x"
|
||||
bdrv_co_io_em(void *bs, int64_t sector_num, int nb_sectors, int is_write, void *acb) "bs %p sector_num %"PRId64" nb_sectors %d is_write %d acb %p"
|
||||
bdrv_co_do_copy_on_readv(void *bs, int64_t sector_num, int nb_sectors, int64_t cluster_sector_num, int cluster_nb_sectors) "bs %p sector_num %"PRId64" nb_sectors %d cluster_sector_num %"PRId64" cluster_nb_sectors %d"
|
||||
|
||||
@ -127,6 +128,7 @@ thread_pool_cancel(void *req, void *opaque) "req %p opaque %p"
|
||||
|
||||
# block/raw-win32.c
|
||||
# block/raw-posix.c
|
||||
paio_submit_co(int64_t sector_num, int nb_sectors, int type) "sector_num %"PRId64" nb_sectors %d type %d"
|
||||
paio_submit(void *acb, void *opaque, int64_t sector_num, int nb_sectors, int type) "acb %p opaque %p sector_num %"PRId64" nb_sectors %d type %d"
|
||||
|
||||
# ioport.c
|
||||
|
Loading…
Reference in New Issue
Block a user