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:
Anthony Liguori 2013-12-07 07:35:31 -08:00
commit 93531372f0
48 changed files with 3246 additions and 396 deletions

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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