提交 93531372 编写于 作者: A Anthony Liguori

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: NAnthony Liguori <aliguori@amazon.com>
...@@ -217,11 +217,6 @@ bool aio_poll(AioContext *ctx, bool blocking) ...@@ -217,11 +217,6 @@ bool aio_poll(AioContext *ctx, bool blocking)
ctx->walking_handlers--; ctx->walking_handlers--;
/* early return if we only have the aio_notify() fd */
if (ctx->pollfds->len == 1) {
return progress;
}
/* wait until next event */ /* wait until next event */
ret = qemu_poll_ns((GPollFD *)ctx->pollfds->data, ret = qemu_poll_ns((GPollFD *)ctx->pollfds->data,
ctx->pollfds->len, ctx->pollfds->len,
......
...@@ -161,11 +161,6 @@ bool aio_poll(AioContext *ctx, bool blocking) ...@@ -161,11 +161,6 @@ bool aio_poll(AioContext *ctx, bool blocking)
ctx->walking_handlers--; ctx->walking_handlers--;
/* early return if we only have the aio_notify() fd */
if (count == 1) {
return progress;
}
/* wait until next event */ /* wait until next event */
while (count > 0) { while (count > 0) {
int ret; int ret;
......
...@@ -79,6 +79,7 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs, ...@@ -79,6 +79,7 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
int64_t sector_num, int64_t sector_num,
QEMUIOVector *qiov, QEMUIOVector *qiov,
int nb_sectors, int nb_sectors,
BdrvRequestFlags flags,
BlockDriverCompletionFunc *cb, BlockDriverCompletionFunc *cb,
void *opaque, void *opaque,
bool is_write); bool is_write);
...@@ -1556,13 +1557,8 @@ void bdrv_drain_all(void) ...@@ -1556,13 +1557,8 @@ void bdrv_drain_all(void)
BlockDriverState *bs; BlockDriverState *bs;
while (busy) { while (busy) {
/* FIXME: We do not have timer support here, so this is effectively
* a busy wait.
*/
QTAILQ_FOREACH(bs, &bdrv_states, list) { QTAILQ_FOREACH(bs, &bdrv_states, list) {
if (bdrv_start_throttled_reqs(bs)) { bdrv_start_throttled_reqs(bs);
busy = true;
}
} }
busy = bdrv_requests_pending_all(); busy = bdrv_requests_pending_all();
...@@ -2770,14 +2766,21 @@ static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, ...@@ -2770,14 +2766,21 @@ static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs,
while (nb_sectors > 0 && !ret) { while (nb_sectors > 0 && !ret) {
int num = nb_sectors; int num = nb_sectors;
/* align request */ /* Align request. Block drivers can expect the "bulk" of the request
if (bs->bl.write_zeroes_alignment && * to be aligned.
num >= bs->bl.write_zeroes_alignment && */
sector_num % bs->bl.write_zeroes_alignment) { if (bs->bl.write_zeroes_alignment
if (num > 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 = 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 */ /* limit request size */
...@@ -2795,16 +2798,20 @@ static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, ...@@ -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 */ /* Fall back to bounce buffer if write zeroes is unsupported */
iov.iov_len = num * BDRV_SECTOR_SIZE; iov.iov_len = num * BDRV_SECTOR_SIZE;
if (iov.iov_base == NULL) { if (iov.iov_base == NULL) {
/* allocate bounce buffer only once and ensure that it iov.iov_base = qemu_blockalign(bs, num * BDRV_SECTOR_SIZE);
* is big enough for this and all future requests. memset(iov.iov_base, 0, num * BDRV_SECTOR_SIZE);
*/
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);
} }
qemu_iovec_init_external(&qiov, &iov, 1); qemu_iovec_init_external(&qiov, &iov, 1);
ret = drv->bdrv_co_writev(bs, sector_num, num, &qiov); 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; sector_num += num;
...@@ -2887,7 +2894,7 @@ int coroutine_fn bdrv_co_write_zeroes(BlockDriverState *bs, ...@@ -2887,7 +2894,7 @@ int coroutine_fn bdrv_co_write_zeroes(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int64_t sector_num, int nb_sectors,
BdrvRequestFlags flags) 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)) { if (!(bs->open_flags & BDRV_O_UNMAP)) {
flags &= ~BDRV_REQ_MAY_UNMAP; flags &= ~BDRV_REQ_MAY_UNMAP;
...@@ -3669,7 +3676,7 @@ BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num, ...@@ -3669,7 +3676,7 @@ BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
{ {
trace_bdrv_aio_readv(bs, sector_num, nb_sectors, opaque); 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); cb, opaque, false);
} }
...@@ -3679,7 +3686,18 @@ BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num, ...@@ -3679,7 +3686,18 @@ BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
{ {
trace_bdrv_aio_writev(bs, sector_num, nb_sectors, opaque); 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); cb, opaque, true);
} }
...@@ -3851,8 +3869,10 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs) ...@@ -3851,8 +3869,10 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
/* Run the aio requests. */ /* Run the aio requests. */
mcb->num_requests = num_reqs; mcb->num_requests = num_reqs;
for (i = 0; i < num_reqs; i++) { for (i = 0; i < num_reqs; i++) {
bdrv_aio_writev(bs, reqs[i].sector, reqs[i].qiov, bdrv_co_aio_rw_vector(bs, reqs[i].sector, reqs[i].qiov,
reqs[i].nb_sectors, multiwrite_cb, mcb); reqs[i].nb_sectors, reqs[i].flags,
multiwrite_cb, mcb,
true);
} }
return 0; return 0;
...@@ -3994,10 +4014,10 @@ static void coroutine_fn bdrv_co_do_rw(void *opaque) ...@@ -3994,10 +4014,10 @@ static void coroutine_fn bdrv_co_do_rw(void *opaque)
if (!acb->is_write) { if (!acb->is_write) {
acb->req.error = bdrv_co_do_readv(bs, acb->req.sector, 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 { } else {
acb->req.error = bdrv_co_do_writev(bs, acb->req.sector, 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); acb->bh = qemu_bh_new(bdrv_co_em_bh, acb);
...@@ -4008,6 +4028,7 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs, ...@@ -4008,6 +4028,7 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
int64_t sector_num, int64_t sector_num,
QEMUIOVector *qiov, QEMUIOVector *qiov,
int nb_sectors, int nb_sectors,
BdrvRequestFlags flags,
BlockDriverCompletionFunc *cb, BlockDriverCompletionFunc *cb,
void *opaque, void *opaque,
bool is_write) bool is_write)
...@@ -4019,6 +4040,7 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs, ...@@ -4019,6 +4040,7 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
acb->req.sector = sector_num; acb->req.sector = sector_num;
acb->req.nb_sectors = nb_sectors; acb->req.nb_sectors = nb_sectors;
acb->req.qiov = qiov; acb->req.qiov = qiov;
acb->req.flags = flags;
acb->is_write = is_write; acb->is_write = is_write;
acb->done = NULL; acb->done = NULL;
...@@ -4302,6 +4324,8 @@ static void coroutine_fn bdrv_discard_co_entry(void *opaque) ...@@ -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 coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
int nb_sectors) int nb_sectors)
{ {
int max_discard;
if (!bs->drv) { if (!bs->drv) {
return -ENOMEDIUM; return -ENOMEDIUM;
} else if (bdrv_check_request(bs, sector_num, nb_sectors)) { } 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, ...@@ -4317,55 +4341,55 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
return 0; return 0;
} }
if (bs->drv->bdrv_co_discard) { if (!bs->drv->bdrv_co_discard && !bs->drv->bdrv_aio_discard) {
int max_discard = bs->bl.max_discard ? return 0;
bs->bl.max_discard : MAX_DISCARD_DEFAULT; }
while (nb_sectors > 0) { max_discard = bs->bl.max_discard ? bs->bl.max_discard : MAX_DISCARD_DEFAULT;
int ret; while (nb_sectors > 0) {
int num = nb_sectors; int ret;
int num = nb_sectors;
/* align request */ /* align request */
if (bs->bl.discard_alignment && if (bs->bl.discard_alignment &&
num >= bs->bl.discard_alignment && num >= bs->bl.discard_alignment &&
sector_num % bs->bl.discard_alignment) { sector_num % bs->bl.discard_alignment) {
if (num > bs->bl.discard_alignment) { if (num > bs->bl.discard_alignment) {
num = bs->bl.discard_alignment; num = bs->bl.discard_alignment;
}
num -= sector_num % bs->bl.discard_alignment;
} }
num -= sector_num % bs->bl.discard_alignment;
}
/* limit request size */ /* limit request size */
if (num > max_discard) { if (num > max_discard) {
num = max_discard; num = max_discard;
} }
if (bs->drv->bdrv_co_discard) {
ret = bs->drv->bdrv_co_discard(bs, sector_num, num); ret = bs->drv->bdrv_co_discard(bs, sector_num, num);
if (ret) { } else {
return ret; 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;
} }
sector_num += num;
nb_sectors -= num;
} }
return 0; if (ret && ret != -ENOTSUP) {
} else if (bs->drv->bdrv_aio_discard) { return ret;
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 {
return 0; sector_num += num;
nb_sectors -= num;
} }
return 0;
} }
int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) 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, ...@@ -4684,7 +4708,6 @@ void bdrv_img_create(const char *filename, const char *fmt,
{ {
QEMUOptionParameter *param = NULL, *create_options = NULL; QEMUOptionParameter *param = NULL, *create_options = NULL;
QEMUOptionParameter *backing_fmt, *backing_file, *size; QEMUOptionParameter *backing_fmt, *backing_file, *size;
BlockDriverState *bs = NULL;
BlockDriver *drv, *proto_drv; BlockDriver *drv, *proto_drv;
BlockDriver *backing_drv = NULL; BlockDriver *backing_drv = NULL;
Error *local_err = NULL; Error *local_err = NULL;
...@@ -4763,6 +4786,7 @@ void bdrv_img_create(const char *filename, const char *fmt, ...@@ -4763,6 +4786,7 @@ void bdrv_img_create(const char *filename, const char *fmt,
size = get_option_parameter(param, BLOCK_OPT_SIZE); size = get_option_parameter(param, BLOCK_OPT_SIZE);
if (size && size->value.n == -1) { if (size && size->value.n == -1) {
if (backing_file && backing_file->value.s) { if (backing_file && backing_file->value.s) {
BlockDriverState *bs;
uint64_t size; uint64_t size;
char buf[32]; char buf[32];
int back_flags; int back_flags;
...@@ -4781,6 +4805,7 @@ void bdrv_img_create(const char *filename, const char *fmt, ...@@ -4781,6 +4805,7 @@ void bdrv_img_create(const char *filename, const char *fmt,
error_get_pretty(local_err)); error_get_pretty(local_err));
error_free(local_err); error_free(local_err);
local_err = NULL; local_err = NULL;
bdrv_unref(bs);
goto out; goto out;
} }
bdrv_get_geometry(bs, &size); bdrv_get_geometry(bs, &size);
...@@ -4788,6 +4813,8 @@ void bdrv_img_create(const char *filename, const char *fmt, ...@@ -4788,6 +4813,8 @@ void bdrv_img_create(const char *filename, const char *fmt,
snprintf(buf, sizeof(buf), "%" PRId64, size); snprintf(buf, sizeof(buf), "%" PRId64, size);
set_option_parameter(param, BLOCK_OPT_SIZE, buf); set_option_parameter(param, BLOCK_OPT_SIZE, buf);
bdrv_unref(bs);
} else { } else {
error_setg(errp, "Image creation needs a size parameter"); error_setg(errp, "Image creation needs a size parameter");
goto out; goto out;
...@@ -4818,9 +4845,6 @@ out: ...@@ -4818,9 +4845,6 @@ out:
free_option_parameters(create_options); free_option_parameters(create_options);
free_option_parameters(param); free_option_parameters(param);
if (bs) {
bdrv_unref(bs);
}
if (error_is_set(&local_err)) { if (error_is_set(&local_err)) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* QEMU Block driver for iSCSI images * QEMU Block driver for iSCSI images
* *
* Copyright (c) 2010-2011 Ronnie Sahlberg <ronniesahlberg@gmail.com> * 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 * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
...@@ -54,6 +55,7 @@ typedef struct IscsiLun { ...@@ -54,6 +55,7 @@ typedef struct IscsiLun {
QEMUTimer *nop_timer; QEMUTimer *nop_timer;
uint8_t lbpme; uint8_t lbpme;
uint8_t lbprz; uint8_t lbprz;
uint8_t has_write_same;
struct scsi_inquiry_logical_block_provisioning lbp; struct scsi_inquiry_logical_block_provisioning lbp;
struct scsi_inquiry_block_limits bl; struct scsi_inquiry_block_limits bl;
unsigned char *zeroblock; unsigned char *zeroblock;
...@@ -975,8 +977,13 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, ...@@ -975,8 +977,13 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num,
return -EINVAL; return -EINVAL;
} }
if (!iscsilun->lbp.lbpws) { if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) {
/* WRITE SAME is not supported by the target */ /* 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; return -ENOTSUP;
} }
...@@ -1011,6 +1018,14 @@ retry: ...@@ -1011,6 +1018,14 @@ retry:
} }
if (iTask.status != SCSI_STATUS_GOOD) { 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; return -EIO;
} }
...@@ -1374,6 +1389,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, ...@@ -1374,6 +1389,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
} }
iscsilun->type = inq->periperal_device_type; iscsilun->type = inq->periperal_device_type;
iscsilun->has_write_same = true;
if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) { if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
goto out; goto out;
...@@ -1441,6 +1457,9 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, ...@@ -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, bs->bl.write_zeroes_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran,
iscsilun); iscsilun);
bs->bl.opt_transfer_length = sector_lun2qemu(iscsilun->bl.opt_xfer_len,
iscsilun);
} }
#if defined(LIBISCSI_FEATURE_NOP_COUNTER) #if defined(LIBISCSI_FEATURE_NOP_COUNTER)
...@@ -1505,11 +1524,6 @@ static int iscsi_truncate(BlockDriverState *bs, int64_t offset) ...@@ -1505,11 +1524,6 @@ static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
return 0; return 0;
} }
static int iscsi_has_zero_init(BlockDriverState *bs)
{
return 0;
}
static int iscsi_create(const char *filename, QEMUOptionParameter *options, static int iscsi_create(const char *filename, QEMUOptionParameter *options,
Error **errp) Error **errp)
{ {
...@@ -1569,6 +1583,13 @@ static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) ...@@ -1569,6 +1583,13 @@ static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
IscsiLun *iscsilun = bs->opaque; IscsiLun *iscsilun = bs->opaque;
bdi->unallocated_blocks_are_zero = !!iscsilun->lbprz; bdi->unallocated_blocks_are_zero = !!iscsilun->lbprz;
bdi->can_write_zeroes_with_unmap = iscsilun->lbprz && iscsilun->lbp.lbpws; 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; return 0;
} }
...@@ -1608,8 +1629,6 @@ static BlockDriver bdrv_iscsi = { ...@@ -1608,8 +1629,6 @@ static BlockDriver bdrv_iscsi = {
.bdrv_aio_writev = iscsi_aio_writev, .bdrv_aio_writev = iscsi_aio_writev,
.bdrv_aio_flush = iscsi_aio_flush, .bdrv_aio_flush = iscsi_aio_flush,
.bdrv_has_zero_init = iscsi_has_zero_init,
#ifdef __linux__ #ifdef __linux__
.bdrv_ioctl = iscsi_ioctl, .bdrv_ioctl = iscsi_ioctl,
.bdrv_aio_ioctl = iscsi_aio_ioctl, .bdrv_aio_ioctl = iscsi_aio_ioctl,
......
...@@ -1401,7 +1401,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, ...@@ -1401,7 +1401,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
/* Round start up and end down */ /* Round start up and end down */
offset = align_offset(offset, s->cluster_size); 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) { if (offset > end_offset) {
return 0; return 0;
......
...@@ -515,8 +515,8 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, ...@@ -515,8 +515,8 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
s->l2_table_cache); s->l2_table_cache);
} }
start = offset & ~(s->cluster_size - 1); start = start_of_cluster(s, offset);
last = (offset + length - 1) & ~(s->cluster_size - 1); last = start_of_cluster(s, offset + length - 1);
for(cluster_offset = start; cluster_offset <= last; for(cluster_offset = start; cluster_offset <= last;
cluster_offset += s->cluster_size) cluster_offset += s->cluster_size)
{ {
...@@ -724,7 +724,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) ...@@ -724,7 +724,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
} }
redo: redo:
free_in_cluster = s->cluster_size - 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) { if (size <= free_in_cluster) {
/* enough space in current cluster */ /* enough space in current cluster */
offset = s->free_byte_offset; offset = s->free_byte_offset;
...@@ -732,7 +732,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) ...@@ -732,7 +732,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
free_in_cluster -= size; free_in_cluster -= size;
if (free_in_cluster == 0) if (free_in_cluster == 0)
s->free_byte_offset = 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_update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
QCOW2_DISCARD_NEVER); QCOW2_DISCARD_NEVER);
} else { } else {
...@@ -740,7 +740,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) ...@@ -740,7 +740,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
if (offset < 0) { if (offset < 0) {
return offset; 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) { if ((cluster_offset + s->cluster_size) == offset) {
/* we are lucky: contiguous data */ /* we are lucky: contiguous data */
offset = s->free_byte_offset; offset = s->free_byte_offset;
...@@ -1010,8 +1010,8 @@ static void inc_refcounts(BlockDriverState *bs, ...@@ -1010,8 +1010,8 @@ static void inc_refcounts(BlockDriverState *bs,
if (size <= 0) if (size <= 0)
return; return;
start = offset & ~(s->cluster_size - 1); start = start_of_cluster(s, offset);
last = (offset + size - 1) & ~(s->cluster_size - 1); last = start_of_cluster(s, offset + size - 1);
for(cluster_offset = start; cluster_offset <= last; for(cluster_offset = start; cluster_offset <= last;
cluster_offset += s->cluster_size) { cluster_offset += s->cluster_size) {
k = cluster_offset >> s->cluster_bits; k = cluster_offset >> s->cluster_bits;
...@@ -1122,7 +1122,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, ...@@ -1122,7 +1122,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
offset, s->cluster_size); offset, s->cluster_size);
/* Correct offsets are cluster aligned */ /* 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 " fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not "
"properly aligned; L2 entry corrupted.\n", offset); "properly aligned; L2 entry corrupted.\n", offset);
res->corruptions++; res->corruptions++;
...@@ -1194,7 +1194,7 @@ static int check_refcounts_l1(BlockDriverState *bs, ...@@ -1194,7 +1194,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
l2_offset, s->cluster_size); l2_offset, s->cluster_size);
/* L2 tables are cluster aligned */ /* 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 " fprintf(stderr, "ERROR l2_offset=%" PRIx64 ": Table is not "
"cluster aligned; L1 entry corrupted\n", l2_offset); "cluster aligned; L1 entry corrupted\n", l2_offset);
res->corruptions++; res->corruptions++;
...@@ -1423,7 +1423,7 @@ static int64_t realloc_refcount_block(BlockDriverState *bs, int reftable_index, ...@@ -1423,7 +1423,7 @@ static int64_t realloc_refcount_block(BlockDriverState *bs, int reftable_index,
} }
/* update refcount table */ /* update refcount table */
assert(!(new_offset & (s->cluster_size - 1))); assert(!offset_into_cluster(s, new_offset));
s->refcount_table[reftable_index] = new_offset; s->refcount_table[reftable_index] = new_offset;
ret = write_reftable_entry(bs, reftable_index); ret = write_reftable_entry(bs, reftable_index);
if (ret < 0) { if (ret < 0) {
...@@ -1507,7 +1507,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, ...@@ -1507,7 +1507,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
cluster = offset >> s->cluster_bits; cluster = offset >> s->cluster_bits;
/* Refcount blocks are cluster aligned */ /* 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 " fprintf(stderr, "ERROR refcount block %" PRId64 " is not "
"cluster aligned; refcount table entry corrupted\n", i); "cluster aligned; refcount table entry corrupted\n", i);
res->corruptions++; res->corruptions++;
......
...@@ -675,7 +675,10 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) ...@@ -675,7 +675,10 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
return s->nb_snapshots; 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; int i, snapshot_index;
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
...@@ -687,8 +690,10 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name) ...@@ -687,8 +690,10 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
assert(bs->read_only); assert(bs->read_only);
/* Search the snapshot */ /* 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) { if (snapshot_index < 0) {
error_setg(errp,
"Can't find snapshot");
return -ENOENT; return -ENOENT;
} }
sn = &s->snapshots[snapshot_index]; sn = &s->snapshots[snapshot_index];
...@@ -699,6 +704,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name) ...@@ -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); ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_table, new_l1_bytes);
if (ret < 0) { if (ret < 0) {
error_setg(errp, "Failed to read l1 table for snapshot");
g_free(new_l1_table); g_free(new_l1_table);
return ret; return ret;
} }
......
...@@ -718,6 +718,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, ...@@ -718,6 +718,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
} }
qemu_opts_del(opts); qemu_opts_del(opts);
bs->bl.write_zeroes_alignment = s->cluster_sectors;
if (s->use_lazy_refcounts && s->qcow_version < 3) { if (s->use_lazy_refcounts && s->qcow_version < 3) {
error_setg(errp, "Lazy refcounts require a qcow2 image with at least " 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, ...@@ -1471,7 +1472,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
* size for any qcow2 image. * size for any qcow2 image.
*/ */
BlockDriverState* bs; BlockDriverState* bs;
QCowHeader header; QCowHeader *header;
uint8_t* refcount_table; uint8_t* refcount_table;
Error *local_err = NULL; Error *local_err = NULL;
int ret; int ret;
...@@ -1489,30 +1490,34 @@ static int qcow2_create2(const char *filename, int64_t total_size, ...@@ -1489,30 +1490,34 @@ static int qcow2_create2(const char *filename, int64_t total_size,
} }
/* Write the header */ /* Write the header */
memset(&header, 0, sizeof(header)); QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
header.magic = cpu_to_be32(QCOW_MAGIC); header = g_malloc0(cluster_size);
header.version = cpu_to_be32(version); *header = (QCowHeader) {
header.cluster_bits = cpu_to_be32(cluster_bits); .magic = cpu_to_be32(QCOW_MAGIC),
header.size = cpu_to_be64(0); .version = cpu_to_be32(version),
header.l1_table_offset = cpu_to_be64(0); .cluster_bits = cpu_to_be32(cluster_bits),
header.l1_size = cpu_to_be32(0); .size = cpu_to_be64(0),
header.refcount_table_offset = cpu_to_be64(cluster_size); .l1_table_offset = cpu_to_be64(0),
header.refcount_table_clusters = cpu_to_be32(1); .l1_size = cpu_to_be32(0),
header.refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT); .refcount_table_offset = cpu_to_be64(cluster_size),
header.header_length = cpu_to_be32(sizeof(header)); .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) { if (flags & BLOCK_FLAG_ENCRYPT) {
header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES); header->crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
} else { } 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) { if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) {
header.compatible_features |= header->compatible_features |=
cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS); 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) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not write qcow2 header"); error_setg_errno(errp, -ret, "Could not write qcow2 header");
goto out; goto out;
...@@ -1893,6 +1898,8 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) ...@@ -1893,6 +1898,8 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
{ {
BDRVQcowState *s = bs->opaque; 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->cluster_size = s->cluster_size;
bdi->vm_state_offset = qcow2_vm_state_offset(s); bdi->vm_state_offset = qcow2_vm_state_offset(s);
return 0; return 0;
......
...@@ -488,7 +488,10 @@ int qcow2_snapshot_delete(BlockDriverState *bs, ...@@ -488,7 +488,10 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
const char *name, const char *name,
Error **errp); Error **errp);
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); 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); void qcow2_free_snapshots(BlockDriverState *bs);
int qcow2_read_snapshots(BlockDriverState *bs); int qcow2_read_snapshots(BlockDriverState *bs);
......
...@@ -495,6 +495,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, ...@@ -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, s->need_check_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
qed_need_check_timer_cb, s); qed_need_check_timer_cb, s);
...@@ -1475,6 +1476,8 @@ static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) ...@@ -1475,6 +1476,8 @@ static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
memset(bdi, 0, sizeof(*bdi)); memset(bdi, 0, sizeof(*bdi));
bdi->cluster_size = s->header.cluster_size; bdi->cluster_size = s->header.cluster_size;
bdi->is_dirty = s->header.features & QED_F_NEED_CHECK; 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; return 0;
} }
......
...@@ -21,9 +21,10 @@ ...@@ -21,9 +21,10 @@
#define QEMU_AIO_IOCTL 0x0004 #define QEMU_AIO_IOCTL 0x0004
#define QEMU_AIO_FLUSH 0x0008 #define QEMU_AIO_FLUSH 0x0008
#define QEMU_AIO_DISCARD 0x0010 #define QEMU_AIO_DISCARD 0x0010
#define QEMU_AIO_WRITE_ZEROES 0x0020
#define QEMU_AIO_TYPE_MASK \ #define QEMU_AIO_TYPE_MASK \
(QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH| \ (QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH| \
QEMU_AIO_DISCARD) QEMU_AIO_DISCARD|QEMU_AIO_WRITE_ZEROES)
/* AIO flags */ /* AIO flags */
#define QEMU_AIO_MISALIGNED 0x1000 #define QEMU_AIO_MISALIGNED 0x1000
......
...@@ -139,9 +139,11 @@ typedef struct BDRVRawState { ...@@ -139,9 +139,11 @@ typedef struct BDRVRawState {
void *aio_ctx; void *aio_ctx;
#endif #endif
#ifdef CONFIG_XFS #ifdef CONFIG_XFS
bool is_xfs : 1; bool is_xfs:1;
#endif #endif
bool has_discard : 1; bool has_discard:1;
bool has_write_zeroes:1;
bool discard_zeroes:1;
} BDRVRawState; } BDRVRawState;
typedef struct BDRVRawReopenState { typedef struct BDRVRawReopenState {
...@@ -283,6 +285,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, ...@@ -283,6 +285,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
Error *local_err = NULL; Error *local_err = NULL;
const char *filename; const char *filename;
int fd, ret; int fd, ret;
struct stat st;
opts = qemu_opts_create_nofail(&raw_runtime_opts); opts = qemu_opts_create_nofail(&raw_runtime_opts);
qemu_opts_absorb_qdict(opts, options, &local_err); qemu_opts_absorb_qdict(opts, options, &local_err);
...@@ -323,10 +326,38 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, ...@@ -323,10 +326,38 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
} }
#endif #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 #ifdef CONFIG_XFS
if (platform_test_xfs_fd(s->fd)) { if (platform_test_xfs_fd(s->fd)) {
s->is_xfs = 1; s->is_xfs = true;
} }
#endif #endif
...@@ -675,6 +706,23 @@ static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb) ...@@ -675,6 +706,23 @@ static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb)
} }
#ifdef CONFIG_XFS #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) static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes)
{ {
struct xfs_flock64 fl; struct xfs_flock64 fl;
...@@ -693,13 +741,49 @@ static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes) ...@@ -693,13 +741,49 @@ static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes)
} }
#endif #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) static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb)
{ {
int ret = -EOPNOTSUPP; int ret = -EOPNOTSUPP;
BDRVRawState *s = aiocb->bs->opaque; BDRVRawState *s = aiocb->bs->opaque;
if (s->has_discard == 0) { if (!s->has_discard) {
return 0; return -ENOTSUP;
} }
if (aiocb->aio_type & QEMU_AIO_BLKDEV) { if (aiocb->aio_type & QEMU_AIO_BLKDEV) {
...@@ -734,8 +818,8 @@ static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb) ...@@ -734,8 +818,8 @@ static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb)
if (ret == -ENODEV || ret == -ENOSYS || ret == -EOPNOTSUPP || if (ret == -ENODEV || ret == -ENOSYS || ret == -EOPNOTSUPP ||
ret == -ENOTTY) { ret == -ENOTTY) {
s->has_discard = 0; s->has_discard = false;
ret = 0; ret = -ENOTSUP;
} }
return ret; return ret;
} }
...@@ -777,6 +861,9 @@ static int aio_worker(void *arg) ...@@ -777,6 +861,9 @@ static int aio_worker(void *arg)
case QEMU_AIO_DISCARD: case QEMU_AIO_DISCARD:
ret = handle_aiocb_discard(aiocb); ret = handle_aiocb_discard(aiocb);
break; break;
case QEMU_AIO_WRITE_ZEROES:
ret = handle_aiocb_write_zeroes(aiocb);
break;
default: default:
fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
ret = -EINVAL; ret = -EINVAL;
...@@ -787,6 +874,29 @@ static int aio_worker(void *arg) ...@@ -787,6 +874,29 @@ static int aio_worker(void *arg)
return ret; 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, static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque, int type) BlockDriverCompletionFunc *cb, void *opaque, int type)
...@@ -1199,6 +1309,31 @@ static coroutine_fn BlockDriverAIOCB *raw_aio_discard(BlockDriverState *bs, ...@@ -1199,6 +1309,31 @@ static coroutine_fn BlockDriverAIOCB *raw_aio_discard(BlockDriverState *bs,
cb, opaque, QEMU_AIO_DISCARD); 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[] = { static QEMUOptionParameter raw_create_options[] = {
{ {
.name = BLOCK_OPT_SIZE, .name = BLOCK_OPT_SIZE,
...@@ -1222,6 +1357,7 @@ static BlockDriver bdrv_file = { ...@@ -1222,6 +1357,7 @@ static BlockDriver bdrv_file = {
.bdrv_create = raw_create, .bdrv_create = raw_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_co_get_block_status = raw_co_get_block_status, .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_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev, .bdrv_aio_writev = raw_aio_writev,
...@@ -1230,6 +1366,7 @@ static BlockDriver bdrv_file = { ...@@ -1230,6 +1366,7 @@ static BlockDriver bdrv_file = {
.bdrv_truncate = raw_truncate, .bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,
.bdrv_get_info = raw_get_info,
.bdrv_get_allocated_file_size .bdrv_get_allocated_file_size
= raw_get_allocated_file_size, = raw_get_allocated_file_size,
...@@ -1525,6 +1662,26 @@ static coroutine_fn BlockDriverAIOCB *hdev_aio_discard(BlockDriverState *bs, ...@@ -1525,6 +1662,26 @@ static coroutine_fn BlockDriverAIOCB *hdev_aio_discard(BlockDriverState *bs,
cb, opaque, QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV); 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, static int hdev_create(const char *filename, QEMUOptionParameter *options,
Error **errp) Error **errp)
{ {
...@@ -1577,6 +1734,7 @@ static BlockDriver bdrv_host_device = { ...@@ -1577,6 +1734,7 @@ static BlockDriver bdrv_host_device = {
.bdrv_reopen_abort = raw_reopen_abort, .bdrv_reopen_abort = raw_reopen_abort,
.bdrv_create = hdev_create, .bdrv_create = hdev_create,
.create_options = raw_create_options, .create_options = raw_create_options,
.bdrv_co_write_zeroes = hdev_co_write_zeroes,
.bdrv_aio_readv = raw_aio_readv, .bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev, .bdrv_aio_writev = raw_aio_writev,
...@@ -1585,6 +1743,7 @@ static BlockDriver bdrv_host_device = { ...@@ -1585,6 +1743,7 @@ static BlockDriver bdrv_host_device = {
.bdrv_truncate = raw_truncate, .bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,
.bdrv_get_info = raw_get_info,
.bdrv_get_allocated_file_size .bdrv_get_allocated_file_size
= raw_get_allocated_file_size, = raw_get_allocated_file_size,
......
...@@ -25,6 +25,24 @@ ...@@ -25,6 +25,24 @@
#include "block/snapshot.h" #include "block/snapshot.h"
#include "block/block_int.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, int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
const char *name) const char *name)
{ {
...@@ -194,7 +212,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, ...@@ -194,7 +212,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
* If only @snapshot_id is specified, delete the first one with id * If only @snapshot_id is specified, delete the first one with id
* @snapshot_id. * @snapshot_id.
* If only @name is specified, delete the first one with name @name. * 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 * 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 * -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs
...@@ -265,18 +283,71 @@ int bdrv_snapshot_list(BlockDriverState *bs, ...@@ -265,18 +283,71 @@ int bdrv_snapshot_list(BlockDriverState *bs,
return -ENOTSUP; 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, int bdrv_snapshot_load_tmp(BlockDriverState *bs,
const char *snapshot_name) const char *snapshot_id,
const char *name,
Error **errp)
{ {
BlockDriver *drv = bs->drv; BlockDriver *drv = bs->drv;
if (!drv) { if (!drv) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs));
return -ENOMEDIUM; return -ENOMEDIUM;
} }
if (!snapshot_id && !name) {
error_setg(errp, "snapshot_id and name are both NULL");
return -EINVAL;
}
if (!bs->read_only) { if (!bs->read_only) {
error_setg(errp, "Device is not readonly");
return -EINVAL; return -EINVAL;
} }
if (drv->bdrv_snapshot_load_tmp) { 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; 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) ...@@ -331,6 +331,7 @@ static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
logout("\n"); logout("\n");
bdi->cluster_size = s->block_size; bdi->cluster_size = s->block_size;
bdi->vm_state_offset = 0; bdi->vm_state_offset = 0;
bdi->unallocated_blocks_are_zero = true;
return 0; return 0;
} }
......
...@@ -1043,6 +1043,18 @@ static void vhdx_block_translate(BDRVVHDXState *s, int64_t sector_num, ...@@ -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, static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov) int nb_sectors, QEMUIOVector *qiov)
...@@ -1885,6 +1897,7 @@ static BlockDriver bdrv_vhdx = { ...@@ -1885,6 +1897,7 @@ static BlockDriver bdrv_vhdx = {
.bdrv_co_readv = vhdx_co_readv, .bdrv_co_readv = vhdx_co_readv,
.bdrv_co_writev = vhdx_co_writev, .bdrv_co_writev = vhdx_co_writev,
.bdrv_create = vhdx_create, .bdrv_create = vhdx_create,
.bdrv_get_info = vhdx_get_info,
.create_options = vhdx_create_options, .create_options = vhdx_create_options,
}; };
......
...@@ -428,6 +428,10 @@ static int vmdk_add_extent(BlockDriverState *bs, ...@@ -428,6 +428,10 @@ static int vmdk_add_extent(BlockDriverState *bs,
extent->l2_size = l2_size; extent->l2_size = l2_size;
extent->cluster_sectors = flat ? sectors : cluster_sectors; 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) { if (s->num_extents > 1) {
extent->end_sector = (*(extent - 1)).end_sector + extent->sectors; extent->end_sector = (*(extent - 1)).end_sector + extent->sectors;
} else { } else {
...@@ -1596,7 +1600,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options, ...@@ -1596,7 +1600,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
Error **errp) Error **errp)
{ {
int fd, idx = 0; int fd, idx = 0;
char desc[BUF_SIZE]; char *desc = NULL;
int64_t total_size = 0, filesize; int64_t total_size = 0, filesize;
const char *adapter_type = NULL; const char *adapter_type = NULL;
const char *backing_file = NULL; const char *backing_file = NULL;
...@@ -1604,7 +1608,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options, ...@@ -1604,7 +1608,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
int flags = 0; int flags = 0;
int ret = 0; int ret = 0;
bool flat, split, compress; bool flat, split, compress;
char ext_desc_lines[BUF_SIZE] = ""; GString *ext_desc_lines;
char path[PATH_MAX], prefix[PATH_MAX], postfix[PATH_MAX]; char path[PATH_MAX], prefix[PATH_MAX], postfix[PATH_MAX];
const int64_t split_size = 0x80000000; /* VMDK has constant split size */ const int64_t split_size = 0x80000000; /* VMDK has constant split size */
const char *desc_extent_line; const char *desc_extent_line;
...@@ -1632,8 +1636,11 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options, ...@@ -1632,8 +1636,11 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
"ddb.geometry.sectors = \"63\"\n" "ddb.geometry.sectors = \"63\"\n"
"ddb.adapterType = \"%s\"\n"; "ddb.adapterType = \"%s\"\n";
ext_desc_lines = g_string_new(NULL);
if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) { if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) {
return -EINVAL; ret = -EINVAL;
goto exit;
} }
/* Read out options */ /* Read out options */
while (options && options->name) { while (options && options->name) {
...@@ -1659,7 +1666,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options, ...@@ -1659,7 +1666,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
strcmp(adapter_type, "lsilogic") && strcmp(adapter_type, "lsilogic") &&
strcmp(adapter_type, "legacyESX")) { strcmp(adapter_type, "legacyESX")) {
error_setg(errp, "Unknown adapter type: '%s'", adapter_type); error_setg(errp, "Unknown adapter type: '%s'", adapter_type);
return -EINVAL; ret = -EINVAL;
goto exit;
} }
if (strcmp(adapter_type, "ide") != 0) { if (strcmp(adapter_type, "ide") != 0) {
/* that's the number of heads with which vmware operates when /* that's the number of heads with which vmware operates when
...@@ -1675,7 +1683,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options, ...@@ -1675,7 +1683,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
strcmp(fmt, "twoGbMaxExtentFlat") && strcmp(fmt, "twoGbMaxExtentFlat") &&
strcmp(fmt, "streamOptimized")) { strcmp(fmt, "streamOptimized")) {
error_setg(errp, "Unknown subformat: '%s'", fmt); error_setg(errp, "Unknown subformat: '%s'", fmt);
return -EINVAL; ret = -EINVAL;
goto exit;
} }
split = !(strcmp(fmt, "twoGbMaxExtentFlat") && split = !(strcmp(fmt, "twoGbMaxExtentFlat") &&
strcmp(fmt, "twoGbMaxExtentSparse")); strcmp(fmt, "twoGbMaxExtentSparse"));
...@@ -1689,22 +1698,25 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options, ...@@ -1689,22 +1698,25 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
} }
if (flat && backing_file) { if (flat && backing_file) {
error_setg(errp, "Flat image can't have backing file"); error_setg(errp, "Flat image can't have backing file");
return -ENOTSUP; ret = -ENOTSUP;
goto exit;
} }
if (flat && zeroed_grain) { if (flat && zeroed_grain) {
error_setg(errp, "Flat image can't enable zeroed grain"); error_setg(errp, "Flat image can't enable zeroed grain");
return -ENOTSUP; ret = -ENOTSUP;
goto exit;
} }
if (backing_file) { if (backing_file) {
BlockDriverState *bs = bdrv_new(""); BlockDriverState *bs = bdrv_new("");
ret = bdrv_open(bs, backing_file, NULL, BDRV_O_NO_BACKING, NULL, errp); ret = bdrv_open(bs, backing_file, NULL, BDRV_O_NO_BACKING, NULL, errp);
if (ret != 0) { if (ret != 0) {
bdrv_unref(bs); bdrv_unref(bs);
return ret; goto exit;
} }
if (strcmp(bs->drv->format_name, "vmdk")) { if (strcmp(bs->drv->format_name, "vmdk")) {
bdrv_unref(bs); bdrv_unref(bs);
return -EINVAL; ret = -EINVAL;
goto exit;
} }
parent_cid = vmdk_read_cid(bs, 0); parent_cid = vmdk_read_cid(bs, 0);
bdrv_unref(bs); bdrv_unref(bs);
...@@ -1738,25 +1750,27 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options, ...@@ -1738,25 +1750,27 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
if (vmdk_create_extent(ext_filename, size, if (vmdk_create_extent(ext_filename, size,
flat, compress, zeroed_grain)) { flat, compress, zeroed_grain)) {
return -EINVAL; ret = -EINVAL;
goto exit;
} }
filesize -= size; filesize -= size;
/* Format description line */ /* Format description line */
snprintf(desc_line, sizeof(desc_line), snprintf(desc_line, sizeof(desc_line),
desc_extent_line, size / 512, desc_filename); 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 */ /* generate descriptor file */
snprintf(desc, sizeof(desc), desc_template, desc = g_strdup_printf(desc_template,
(unsigned int)time(NULL), (unsigned int)time(NULL),
parent_cid, parent_cid,
fmt, fmt,
parent_desc_line, parent_desc_line,
ext_desc_lines, ext_desc_lines->str,
(flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
total_size / (int64_t)(63 * number_heads * 512), number_heads, total_size / (int64_t)(63 * number_heads * 512),
adapter_type); number_heads,
adapter_type);
if (split || flat) { if (split || flat) {
fd = qemu_open(filename, fd = qemu_open(filename,
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
...@@ -1767,21 +1781,25 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options, ...@@ -1767,21 +1781,25 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
0644); 0644);
} }
if (fd < 0) { if (fd < 0) {
return -errno; ret = -errno;
goto exit;
} }
/* the descriptor offset = 0x200 */ /* the descriptor offset = 0x200 */
if (!split && !flat && 0x200 != lseek(fd, 0x200, SEEK_SET)) { if (!split && !flat && 0x200 != lseek(fd, 0x200, SEEK_SET)) {
ret = -errno; ret = -errno;
goto exit; goto close_exit;
} }
ret = qemu_write_full(fd, desc, strlen(desc)); ret = qemu_write_full(fd, desc, strlen(desc));
if (ret != strlen(desc)) { if (ret != strlen(desc)) {
ret = -errno; ret = -errno;
goto exit; goto close_exit;
} }
ret = 0; ret = 0;
exit: close_exit:
qemu_close(fd); qemu_close(fd);
exit:
g_free(desc);
g_string_free(ext_desc_lines, true);
return ret; return ret;
} }
......
...@@ -455,6 +455,19 @@ fail: ...@@ -455,6 +455,19 @@ fail:
return -1; 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, static int vpc_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors) uint8_t *buf, int nb_sectors)
{ {
...@@ -857,6 +870,8 @@ static BlockDriver bdrv_vpc = { ...@@ -857,6 +870,8 @@ static BlockDriver bdrv_vpc = {
.bdrv_read = vpc_co_read, .bdrv_read = vpc_co_read,
.bdrv_write = vpc_co_write, .bdrv_write = vpc_co_write,
.bdrv_get_info = vpc_get_info,
.create_options = vpc_create_options, .create_options = vpc_create_options,
.bdrv_has_zero_init = vpc_has_zero_init, .bdrv_has_zero_init = vpc_has_zero_init,
}; };
......
...@@ -41,6 +41,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) ...@@ -41,6 +41,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#include <scsi/sg.h> #include <scsi/sg.h>
#endif #endif
#define SCSI_WRITE_SAME_MAX 524288
#define SCSI_DMA_BUF_SIZE 131072 #define SCSI_DMA_BUF_SIZE 131072
#define SCSI_MAX_INQUIRY_LEN 256 #define SCSI_MAX_INQUIRY_LEN 256
#define SCSI_MAX_MODE_LEN 256 #define SCSI_MAX_MODE_LEN 256
...@@ -634,6 +635,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) ...@@ -634,6 +635,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
buflen = 0x40; buflen = 0x40;
memset(outbuf + 4, 0, buflen - 4); memset(outbuf + 4, 0, buflen - 4);
outbuf[4] = 0x1; /* wsnz */
/* optimal transfer length granularity */ /* optimal transfer length granularity */
outbuf[6] = (min_io_size >> 8) & 0xff; outbuf[6] = (min_io_size >> 8) & 0xff;
outbuf[7] = min_io_size & 0xff; outbuf[7] = min_io_size & 0xff;
...@@ -1543,10 +1546,16 @@ done: ...@@ -1543,10 +1546,16 @@ done:
static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
{ {
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint8_t *p = inbuf; uint8_t *p = inbuf;
int len = r->req.cmd.xfer; int len = r->req.cmd.xfer;
UnmapCBData *data; UnmapCBData *data;
/* Reject ANCHOR=1. */
if (r->req.cmd.buf[1] & 0x1) {
goto invalid_field;
}
if (len < 8) { if (len < 8) {
goto invalid_param_len; goto invalid_param_len;
} }
...@@ -1560,6 +1569,11 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) ...@@ -1560,6 +1569,11 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
goto invalid_param_len; 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 = g_new0(UnmapCBData, 1);
data->r = r; data->r = r;
data->inbuf = &p[8]; data->inbuf = &p[8];
...@@ -1572,6 +1586,115 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) ...@@ -1572,6 +1586,115 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
invalid_param_len: invalid_param_len:
scsi_check_condition(r, SENSE_CODE(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) static void scsi_disk_emulate_write_data(SCSIRequest *req)
...@@ -1597,6 +1720,10 @@ 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); scsi_disk_emulate_unmap(r, r->iov.iov_base);
break; break;
case WRITE_SAME_10:
case WRITE_SAME_16:
scsi_disk_emulate_write_same(r, r->iov.iov_base);
break;
default: default:
abort(); abort();
} }
...@@ -1839,29 +1966,10 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) ...@@ -1839,29 +1966,10 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
break; break;
case WRITE_SAME_10: case WRITE_SAME_10:
case WRITE_SAME_16: case WRITE_SAME_16:
nb_sectors = scsi_data_cdb_length(r->req.cmd.buf); DPRINTF("WRITE SAME %d (len %lu)\n",
if (bdrv_is_read_only(s->qdev.conf.bs)) { req->cmd.buf[0] == WRITE_SAME_10 ? 10 : 16,
scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); (long)r->req.cmd.xfer);
return 0; break;
}
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;
default: default:
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
......
...@@ -216,6 +216,9 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num, ...@@ -216,6 +216,9 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors); const uint8_t *buf, int nb_sectors);
int bdrv_write_zeroes(BlockDriverState *bs, int64_t sector_num, int bdrv_write_zeroes(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, BdrvRequestFlags flags); 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_make_zero(BlockDriverState *bs, BdrvRequestFlags flags);
int bdrv_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov); int bdrv_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov);
int bdrv_pread(BlockDriverState *bs, int64_t offset, int bdrv_pread(BlockDriverState *bs, int64_t offset,
...@@ -311,6 +314,7 @@ typedef struct BlockRequest { ...@@ -311,6 +314,7 @@ typedef struct BlockRequest {
/* Fields to be filled by multiwrite caller */ /* Fields to be filled by multiwrite caller */
int64_t sector; int64_t sector;
int nb_sectors; int nb_sectors;
int flags;
QEMUIOVector *qiov; QEMUIOVector *qiov;
BlockDriverCompletionFunc *cb; BlockDriverCompletionFunc *cb;
void *opaque; void *opaque;
......
...@@ -176,7 +176,9 @@ struct BlockDriver { ...@@ -176,7 +176,9 @@ struct BlockDriver {
int (*bdrv_snapshot_list)(BlockDriverState *bs, int (*bdrv_snapshot_list)(BlockDriverState *bs,
QEMUSnapshotInfo **psn_info); QEMUSnapshotInfo **psn_info);
int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs, 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); int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs); ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs);
...@@ -245,6 +247,9 @@ typedef struct BlockLimits { ...@@ -245,6 +247,9 @@ typedef struct BlockLimits {
/* optimal alignment for write zeroes requests in sectors */ /* optimal alignment for write zeroes requests in sectors */
int64_t write_zeroes_alignment; int64_t write_zeroes_alignment;
/* optimal transfer length in sectors */
int opt_transfer_length;
} BlockLimits; } BlockLimits;
/* /*
......
...@@ -105,7 +105,6 @@ bool qemu_in_coroutine(void); ...@@ -105,7 +105,6 @@ bool qemu_in_coroutine(void);
*/ */
typedef struct CoQueue { typedef struct CoQueue {
QTAILQ_HEAD(, Coroutine) entries; QTAILQ_HEAD(, Coroutine) entries;
AioContext *ctx;
} CoQueue; } CoQueue;
/** /**
...@@ -120,12 +119,6 @@ void qemu_co_queue_init(CoQueue *queue); ...@@ -120,12 +119,6 @@ void qemu_co_queue_init(CoQueue *queue);
*/ */
void coroutine_fn qemu_co_queue_wait(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. * Restarts the next coroutine in the CoQueue and removes it from the queue.
* *
......
...@@ -27,6 +27,14 @@ ...@@ -27,6 +27,14 @@
#include "qemu-common.h" #include "qemu-common.h"
#include "qapi/error.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 { typedef struct QEMUSnapshotInfo {
char id_str[128]; /* unique snapshot id */ char id_str[128]; /* unique snapshot id */
...@@ -61,5 +69,10 @@ void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs, ...@@ -61,5 +69,10 @@ void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
int bdrv_snapshot_list(BlockDriverState *bs, int bdrv_snapshot_list(BlockDriverState *bs,
QEMUSnapshotInfo **psn_info); QEMUSnapshotInfo **psn_info);
int bdrv_snapshot_load_tmp(BlockDriverState *bs, 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 #endif
...@@ -41,14 +41,6 @@ void coroutine_fn qemu_co_queue_wait(CoQueue *queue) ...@@ -41,14 +41,6 @@ void coroutine_fn qemu_co_queue_wait(CoQueue *queue)
assert(qemu_in_coroutine()); 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: * qemu_co_queue_run_restart:
* *
......
...@@ -34,9 +34,9 @@ STEXI ...@@ -34,9 +34,9 @@ STEXI
ETEXI ETEXI
DEF("convert", img_convert, 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 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 ETEXI
DEF("info", img_info, DEF("info", img_info,
......
...@@ -93,6 +93,11 @@ static void help(void) ...@@ -93,6 +93,11 @@ static void help(void)
" 'options' is a comma separated list of format specific options in a\n" " '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" " name=value format. Use -o ? for an overview of the options supported by the\n"
" used format\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" " '-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" " '-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" " match exactly. The image doesn't need a working backing file before\n"
...@@ -105,7 +110,6 @@ static void help(void) ...@@ -105,7 +110,6 @@ static void help(void)
" conversion. If the number of bytes is 0, the source will not be scanned for\n" " 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" " unallocated or zero sectors, and the destination image will always be\n"
" fully allocated\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" " '--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" " '-n' skips the target volume creation (useful if the volume is created\n"
" prior to running qemu-img)\n" " prior to running qemu-img)\n"
...@@ -1125,25 +1129,27 @@ out3: ...@@ -1125,25 +1129,27 @@ out3:
static int img_convert(int argc, char **argv) static int img_convert(int argc, char **argv)
{ {
int c, ret = 0, n, n1, bs_n, bs_i, compress, cluster_size, int c, n, n1, bs_n, bs_i, compress, cluster_sectors, skip_create;
cluster_sectors, skip_create; int64_t ret = 0;
int progress = 0, flags; int progress = 0, flags;
const char *fmt, *out_fmt, *cache, *out_baseimg, *out_filename; const char *fmt, *out_fmt, *cache, *out_baseimg, *out_filename;
BlockDriver *drv, *proto_drv; BlockDriver *drv, *proto_drv;
BlockDriverState **bs = NULL, *out_bs = NULL; 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; uint64_t bs_sectors;
uint8_t * buf = NULL; uint8_t * buf = NULL;
size_t bufsectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE;
const uint8_t *buf1; const uint8_t *buf1;
BlockDriverInfo bdi; BlockDriverInfo bdi;
QEMUOptionParameter *param = NULL, *create_options = NULL; QEMUOptionParameter *param = NULL, *create_options = NULL;
QEMUOptionParameter *out_baseimg_param; QEMUOptionParameter *out_baseimg_param;
char *options = NULL; char *options = NULL;
const char *snapshot_name = NULL; const char *snapshot_name = NULL;
float local_progress = 0;
int min_sparse = 8; /* Need at least 4k of zeros for sparse detection */ int min_sparse = 8; /* Need at least 4k of zeros for sparse detection */
bool quiet = false; bool quiet = false;
Error *local_err = NULL; Error *local_err = NULL;
QemuOpts *sn_opts = NULL;
fmt = NULL; fmt = NULL;
out_fmt = "raw"; out_fmt = "raw";
...@@ -1152,7 +1158,7 @@ static int img_convert(int argc, char **argv) ...@@ -1152,7 +1158,7 @@ static int img_convert(int argc, char **argv)
compress = 0; compress = 0;
skip_create = 0; skip_create = 0;
for(;;) { 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) { if (c == -1) {
break; break;
} }
...@@ -1187,6 +1193,18 @@ static int img_convert(int argc, char **argv) ...@@ -1187,6 +1193,18 @@ static int img_convert(int argc, char **argv)
case 's': case 's':
snapshot_name = optarg; snapshot_name = optarg;
break; 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': case 'S':
{ {
int64_t sval; int64_t sval;
...@@ -1227,7 +1245,7 @@ static int img_convert(int argc, char **argv) ...@@ -1227,7 +1245,7 @@ static int img_convert(int argc, char **argv)
out_filename = argv[argc - 1]; out_filename = argv[argc - 1];
/* Initialize before goto out */ /* Initialize before goto out */
qemu_progress_init(progress, 2.0); qemu_progress_init(progress, 1.0);
if (options && is_help_option(options)) { if (options && is_help_option(options)) {
ret = print_block_option_help(out_filename, out_fmt); ret = print_block_option_help(out_filename, out_fmt);
...@@ -1258,17 +1276,26 @@ static int img_convert(int argc, char **argv) ...@@ -1258,17 +1276,26 @@ static int img_convert(int argc, char **argv)
total_sectors += bs_sectors; 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) { if (bs_n > 1) {
error_report("No support for concatenating multiple snapshot"); error_report("No support for concatenating multiple snapshot");
ret = -1; ret = -1;
goto out; goto out;
} }
if (bdrv_snapshot_load_tmp(bs[0], snapshot_name) < 0) {
error_report("Failed to load snapshot"); bdrv_snapshot_load_tmp_by_id_or_name(bs[0], snapshot_name, &local_err);
ret = -1; }
goto out; 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 */ /* Find driver and parse its options */
...@@ -1371,7 +1398,16 @@ static int img_convert(int argc, char **argv) ...@@ -1371,7 +1398,16 @@ static int img_convert(int argc, char **argv)
bs_i = 0; bs_i = 0;
bs_offset = 0; bs_offset = 0;
bdrv_get_geometry(bs[0], &bs_sectors); 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) { if (skip_create) {
int64_t output_length = bdrv_getlength(out_bs); int64_t output_length = bdrv_getlength(out_bs);
...@@ -1387,26 +1423,26 @@ static int img_convert(int argc, char **argv) ...@@ -1387,26 +1423,26 @@ static int img_convert(int argc, char **argv)
} }
} }
if (compress) { cluster_sectors = 0;
ret = bdrv_get_info(out_bs, &bdi); ret = bdrv_get_info(out_bs, &bdi);
if (ret < 0) { if (ret < 0) {
if (compress) {
error_report("could not get block driver info"); error_report("could not get block driver info");
goto out; goto out;
} }
cluster_size = bdi.cluster_size; } else {
if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE) { cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE;
}
if (compress) {
if (cluster_sectors <= 0 || cluster_sectors > bufsectors) {
error_report("invalid cluster size"); error_report("invalid cluster size");
ret = -1; ret = -1;
goto out; goto out;
} }
cluster_sectors = cluster_size >> 9;
sector_num = 0; sector_num = 0;
nb_sectors = total_sectors; nb_sectors = total_sectors;
if (nb_sectors != 0) {
local_progress = (float)100 /
(nb_sectors / MIN(nb_sectors, cluster_sectors));
}
for(;;) { for(;;) {
int64_t bs_num; int64_t bs_num;
...@@ -1464,7 +1500,7 @@ static int img_convert(int argc, char **argv) ...@@ -1464,7 +1500,7 @@ static int img_convert(int argc, char **argv)
} }
} }
sector_num += n; sector_num += n;
qemu_progress_print(local_progress, 100); qemu_progress_print(100.0 * sector_num / total_sectors, 0);
} }
/* signal EOF to align */ /* signal EOF to align */
bdrv_write_compressed(out_bs, 0, NULL, 0); bdrv_write_compressed(out_bs, 0, NULL, 0);
...@@ -1481,21 +1517,13 @@ static int img_convert(int argc, char **argv) ...@@ -1481,21 +1517,13 @@ static int img_convert(int argc, char **argv)
sector_num = 0; // total number of sectors converted so far sector_num = 0; // total number of sectors converted so far
nb_sectors = total_sectors - sector_num; nb_sectors = total_sectors - sector_num;
if (nb_sectors != 0) {
local_progress = (float)100 /
(nb_sectors / MIN(nb_sectors, IO_BUF_SIZE / 512));
}
for(;;) { for(;;) {
nb_sectors = total_sectors - sector_num; nb_sectors = total_sectors - sector_num;
if (nb_sectors <= 0) { if (nb_sectors <= 0) {
ret = 0;
break; break;
} }
if (nb_sectors >= (IO_BUF_SIZE / 512)) {
n = (IO_BUF_SIZE / 512);
} else {
n = nb_sectors;
}
while (sector_num - bs_offset >= bs_sectors) { while (sector_num - bs_offset >= bs_sectors) {
bs_i ++; bs_i ++;
...@@ -1507,34 +1535,59 @@ static int img_convert(int argc, char **argv) ...@@ -1507,34 +1535,59 @@ static int img_convert(int argc, char **argv)
sector_num, bs_i, bs_offset, bs_sectors); */ sector_num, bs_i, bs_offset, bs_sectors); */
} }
if (n > bs_offset + bs_sectors - sector_num) { if ((out_baseimg || has_zero_init) &&
n = bs_offset + bs_sectors - sector_num; 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,
/* If the output image is being created as a copy on write image, n, &n1);
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 (ret < 0) { if (ret < 0) {
error_report("error while reading metadata for sector " error_report("error while reading block status of sector %"
"%" PRId64 ": %s", PRId64 ": %s", sector_num - bs_offset,
sector_num - bs_offset, strerror(-ret)); strerror(-ret));
goto out; 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; sector_num += n1;
continue; continue;
} }
/* The next 'n1' sectors are allocated in the input image. Copy /* If the output image is being created as a copy on write
only those as they may be followed by unallocated sectors. */ * image, assume that sectors which are unallocated in the
n = n1; * input image are present in both the output's and input's
} else { * base images (no need to copy them). */
n1 = n; 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); ret = bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n);
if (ret < 0) { if (ret < 0) {
error_report("error while reading sector %" PRId64 ": %s", error_report("error while reading sector %" PRId64 ": %s",
...@@ -1559,14 +1612,20 @@ static int img_convert(int argc, char **argv) ...@@ -1559,14 +1612,20 @@ static int img_convert(int argc, char **argv)
n -= n1; n -= n1;
buf1 += n1 * 512; buf1 += n1 * 512;
} }
qemu_progress_print(local_progress, 100); qemu_progress_print(100.0 * sector_num / total_sectors, 0);
} }
} }
out: out:
if (!ret) {
qemu_progress_print(100, 0);
}
qemu_progress_end(); qemu_progress_end();
free_option_parameters(create_options); free_option_parameters(create_options);
free_option_parameters(param); free_option_parameters(param);
qemu_vfree(buf); qemu_vfree(buf);
if (sn_opts) {
qemu_opts_del(sn_opts);
}
if (out_bs) { if (out_bs) {
bdrv_unref(out_bs); bdrv_unref(out_bs);
} }
......
...@@ -46,7 +46,11 @@ is the destination disk image filename ...@@ -46,7 +46,11 @@ is the destination disk image filename
is a comma separated list of format specific options in a is a comma separated list of format specific options in a
name=value format. Use @code{-o ?} for an overview of the options supported 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. 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 @item -c
indicates that target image must be compressed (qcow format only) indicates that target image must be compressed (qcow format only)
...@@ -179,10 +183,10 @@ Error on reading data ...@@ -179,10 +183,10 @@ Error on reading data
@end table @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} Convert the disk image @var{filename} or a snapshot @var{snapshot_param}(@var{snapshot_id_or_name} is deprecated)
using format @var{output_fmt}. It can be optionally compressed (@code{-c} 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). option) or use any format specific options like encryption (@code{-o} option).
Only the formats @code{qcow} and @code{qcow2} support compression. The Only the formats @code{qcow} and @code{qcow2} support compression. The
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "block/block.h" #include "block/block.h"
#include "block/nbd.h" #include "block/nbd.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "block/snapshot.h"
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
...@@ -78,8 +79,16 @@ static void usage(const char *name) ...@@ -78,8 +79,16 @@ static void usage(const char *name)
#endif #endif
"\n" "\n"
"Block device options:\n" "Block device options:\n"
" -f, --format=FORMAT set image format (raw, qcow2, ...)\n"
" -r, --read-only export read-only\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" " -n, --nocache disable host cache\n"
" --cache=MODE set cache mode (none, writeback, ...)\n" " --cache=MODE set cache mode (none, writeback, ...)\n"
#ifdef CONFIG_LINUX_AIO #ifdef CONFIG_LINUX_AIO
...@@ -315,7 +324,9 @@ int main(int argc, char **argv) ...@@ -315,7 +324,9 @@ int main(int argc, char **argv)
char *device = NULL; char *device = NULL;
int port = NBD_DEFAULT_PORT; int port = NBD_DEFAULT_PORT;
off_t fd_size; 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[] = { struct option lopt[] = {
{ "help", 0, NULL, 'h' }, { "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' }, { "version", 0, NULL, 'V' },
...@@ -328,6 +339,7 @@ int main(int argc, char **argv) ...@@ -328,6 +339,7 @@ int main(int argc, char **argv)
{ "connect", 1, NULL, 'c' }, { "connect", 1, NULL, 'c' },
{ "disconnect", 0, NULL, 'd' }, { "disconnect", 0, NULL, 'd' },
{ "snapshot", 0, NULL, 's' }, { "snapshot", 0, NULL, 's' },
{ "load-snapshot", 1, NULL, 'l' },
{ "nocache", 0, NULL, 'n' }, { "nocache", 0, NULL, 'n' },
{ "cache", 1, NULL, QEMU_NBD_OPT_CACHE }, { "cache", 1, NULL, QEMU_NBD_OPT_CACHE },
#ifdef CONFIG_LINUX_AIO #ifdef CONFIG_LINUX_AIO
...@@ -428,6 +440,17 @@ int main(int argc, char **argv) ...@@ -428,6 +440,17 @@ int main(int argc, char **argv)
errx(EXIT_FAILURE, "Offset must be positive `%s'", optarg); errx(EXIT_FAILURE, "Offset must be positive `%s'", optarg);
} }
break; 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': case 'r':
nbdflags |= NBD_FLAG_READ_ONLY; nbdflags |= NBD_FLAG_READ_ONLY;
flags &= ~BDRV_O_RDWR; flags &= ~BDRV_O_RDWR;
...@@ -581,6 +604,22 @@ int main(int argc, char **argv) ...@@ -581,6 +604,22 @@ int main(int argc, char **argv)
error_get_pretty(local_err)); 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); fd_size = bdrv_getlength(bs);
if (partition != -1) { if (partition != -1) {
...@@ -641,6 +680,10 @@ int main(int argc, char **argv) ...@@ -641,6 +680,10 @@ int main(int argc, char **argv)
unlink(sockpath); unlink(sockpath);
} }
if (sn_opts) {
qemu_opts_del(sn_opts);
}
if (device) { if (device) {
void *ret; void *ret;
pthread_join(client_thread, &ret); pthread_join(client_thread, &ret);
......
...@@ -22,12 +22,20 @@ Export QEMU disk image using NBD protocol. ...@@ -22,12 +22,20 @@ Export QEMU disk image using NBD protocol.
interface to bind to (default @samp{0.0.0.0}) interface to bind to (default @samp{0.0.0.0})
@item -k, --socket=@var{path} @item -k, --socket=@var{path}
Use a unix socket with path @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 @item -r, --read-only
export read-only export read-only
@item -P, --partition=@var{num} @item -P, --partition=@var{num}
only expose partition @var{num} only expose partition @var{num}
@item -s, --snapshot @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 @item -n, --nocache
@itemx --cache=@var{cache} @itemx --cache=@var{cache}
set cache mode to be used with the file. See the documentation of 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 ...@@ -44,7 +44,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow2 _supported_fmt qcow2
_supported_proto generic _supported_proto generic
_supported_os Linux _supported_os Linux
_default_cache_mode "writethrough"
_supported_cache_modes "writethrough" "none"
echo "Errors while writing 128 kB" echo "Errors while writing 128 kB"
echo echo
......
...@@ -44,7 +44,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 ...@@ -44,7 +44,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow2 _supported_fmt qcow2
_supported_proto generic _supported_proto generic
_supported_os Linux _supported_os Linux
_unsupported_qemu_io_options --nocache _default_cache_mode "writethrough"
_supported_cache_modes "writethrough"
size=128M size=128M
......
...@@ -81,32 +81,5 @@ cp "$TEST_IMG" "$TEST_IMG2" ...@@ -81,32 +81,5 @@ cp "$TEST_IMG" "$TEST_IMG2"
io_pattern write 512 512 0 1 101 io_pattern write 512 512 0 1 101
_compare _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 # Cleanup
status=0 status=0
...@@ -37,20 +37,4 @@ wrote 512/512 bytes at offset 512 ...@@ -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) 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Content mismatch at offset 512! Content mismatch at offset 512!
1 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 Cleanup
...@@ -39,12 +39,12 @@ ide0-hd0: TEST_DIR/t.qcow2 (qcow2) ...@@ -39,12 +39,12 @@ ide0-hd0: TEST_DIR/t.qcow2 (qcow2)
=== Enable and disable lazy refcounting on the command line, plus some invalid values === === 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 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts= 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' 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 ...@@ -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 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 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
=== No medium === === No medium ===
Testing: -drive if=floppy Testing: -drive if=floppy
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive if=ide,media=cdrom Testing: -drive if=ide,media=cdrom
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive if=scsi,media=cdrom Testing: -drive if=scsi,media=cdrom
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive if=ide 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) QEMU_PROG: Device needs media, but drive is empty
QEMU_PROG: Device initialization failed. QEMU_PROG: Device initialization failed.
QEMU_PROG: Initialization of device ide-hd failed QEMU_PROG: Initialization of device ide-hd failed
Testing: -drive if=virtio 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) 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 initialization failed. QEMU_PROG: -drive if=virtio: Device initialization failed.
QEMU_PROG: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized QEMU_PROG: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized
Testing: -drive if=scsi 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) QEMU_PROG: -drive if=scsi: Device needs media, but drive is empty
QEMU_PROG: Device initialization failed. QEMU_PROG: Device initialization failed.
QEMU_PROG: Initialization of device lsi53c895a failed QEMU_PROG: Initialization of device lsi53c895a failed
Testing: -drive if=none,id=disk -device ide-cd,drive=disk Testing: -drive if=none,id=disk -device ide-cd,drive=disk
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive if=none,id=disk -device ide-drive,drive=disk 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) 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 initialization failed.
QEMU_PROG: -device ide-drive,drive=disk: Device 'ide-drive' could not be initialized 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 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) 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 initialization failed.
QEMU_PROG: -device ide-hd,drive=disk: Device 'ide-hd' could not be initialized 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 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) 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 initialization failed.
QEMU_PROG: -device scsi-disk,drive=disk: Device 'scsi-disk' could not be initialized 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 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) 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 initialization failed.
QEMU_PROG: -device scsi-hd,drive=disk: Device 'scsi-hd' could not be initialized 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 ...@@ -136,81 +136,81 @@ QEMU_PROG: -device scsi-hd,drive=disk: Device 'scsi-hd' could not be initialized
=== Read-only === === Read-only ===
Testing: -drive file=TEST_DIR/t.qcow2,if=floppy,readonly=on Testing: -drive file=TEST_DIR/t.qcow2,if=floppy,readonly=on
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,if=ide,media=cdrom,readonly=on Testing: -drive file=TEST_DIR/t.qcow2,if=ide,media=cdrom,readonly=on
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,media=cdrom,readonly=on Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,media=cdrom,readonly=on
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on 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) QEMU_PROG: Can't use a read-only drive
QEMU_PROG: Device initialization failed. QEMU_PROG: Device initialization failed.
QEMU_PROG: Initialization of device ide-hd failed QEMU_PROG: Initialization of device ide-hd failed
Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,readonly=on Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,readonly=on
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-cd,drive=disk 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 X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk 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 X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-drive,drive=disk 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) 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 initialization failed.
QEMU_PROG: -device ide-drive,drive=disk: Device 'ide-drive' could not be initialized 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 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) 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 initialization failed.
QEMU_PROG: -device ide-hd,drive=disk: Device 'ide-hd' could not be initialized 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 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 X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk 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 X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
=== Cache modes === === Cache modes ===
Testing: -drive media=cdrom,cache=none Testing: -drive media=cdrom,cache=none
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive media=cdrom,cache=directsync Testing: -drive media=cdrom,cache=directsync
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive media=cdrom,cache=writeback Testing: -drive media=cdrom,cache=writeback
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive media=cdrom,cache=writethrough Testing: -drive media=cdrom,cache=writethrough
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive media=cdrom,cache=unsafe Testing: -drive media=cdrom,cache=unsafe
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive media=cdrom,cache=invalid_value Testing: -drive media=cdrom,cache=invalid_value
QEMU_PROG: -drive media=cdrom,cache=invalid_value: invalid cache option 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 ...@@ -219,8 +219,8 @@ QEMU_PROG: -drive media=cdrom,cache=invalid_value: invalid cache option
=== Specifying the protocol layer === === Specifying the protocol layer ===
Testing: -drive file=TEST_DIR/t.qcow2,file.driver=file Testing: -drive file=TEST_DIR/t.qcow2,file.driver=file
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2 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 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 ...@@ -41,8 +41,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt generic _supported_fmt generic
_supported_proto generic _supported_proto generic
_supported_os Linux _supported_os Linux
_unsupported_qemu_io_options --nocache _default_cache_mode "writethrough"
_supported_cache_modes "writethrough"
size=128M size=128M
_make_test_img $size _make_test_img $size
......
#!/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
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 ...@@ -75,6 +75,11 @@ echo
echo "=== Testing monolithicFlat with zeroed_grain ===" echo "=== Testing monolithicFlat with zeroed_grain ==="
IMGOPTS="subformat=monolithicFlat,zeroed_grain=on" _make_test_img 2G 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
echo "=== Testing version 3 ===" echo "=== Testing version 3 ==="
_use_sample_img iotest-version3.vmdk.bz2 _use_sample_img iotest-version3.vmdk.bz2
......
此差异已折叠。
#!/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
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 ...@@ -161,6 +161,7 @@ cat <<EOF
QEMU -- $QEMU QEMU -- $QEMU
QEMU_IMG -- $QEMU_IMG QEMU_IMG -- $QEMU_IMG
QEMU_IO -- $QEMU_IO QEMU_IO -- $QEMU_IO
QEMU_NBD -- $QEMU_NBD
IMGFMT -- $FULL_IMGFMT_DETAILS IMGFMT -- $FULL_IMGFMT_DETAILS
IMGPROTO -- $FULL_IMGPROTO_DETAILS IMGPROTO -- $FULL_IMGPROTO_DETAILS
PLATFORM -- $FULL_HOST_DETAILS PLATFORM -- $FULL_HOST_DETAILS
...@@ -242,7 +243,7 @@ do ...@@ -242,7 +243,7 @@ do
fi fi
reference=$seq.out 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 [ -f $seq.out.nocache ] && reference=$seq.out.nocache
fi fi
......
...@@ -42,13 +42,16 @@ expunge=true ...@@ -42,13 +42,16 @@ expunge=true
have_test_arg=false have_test_arg=false
randomize=false randomize=false
valgrind=false valgrind=false
cachemode=false
rm -f $tmp.list $tmp.tmp $tmp.sed rm -f $tmp.list $tmp.tmp $tmp.sed
export IMGFMT=raw export IMGFMT=raw
export IMGFMT_GENERIC=true export IMGFMT_GENERIC=true
export IMGPROTO=file export IMGPROTO=file
export IMGOPTS="" export IMGOPTS=""
export CACHEMODE="writeback"
export QEMU_IO_OPTIONS="" export QEMU_IO_OPTIONS=""
export CACHEMODE_IS_DEFAULT=true
for r for r
do do
...@@ -113,7 +116,12 @@ s/ .*//p ...@@ -113,7 +116,12 @@ s/ .*//p
IMGOPTS="$r" IMGOPTS="$r"
imgopts=false imgopts=false
continue continue
elif $cachemode
then
CACHEMODE="$r"
CACHEMODE_IS_DEFAULT=false
cachemode=false
continue
fi fi
xpand=true xpand=true
...@@ -124,7 +132,7 @@ s/ .*//p ...@@ -124,7 +132,7 @@ s/ .*//p
echo "Usage: $0 [options] [testlist]"' echo "Usage: $0 [options] [testlist]"'
common options common options
-v verbose -v verbose
check options check options
-raw test raw (default) -raw test raw (default)
...@@ -140,19 +148,20 @@ check options ...@@ -140,19 +148,20 @@ check options
-sheepdog test sheepdog -sheepdog test sheepdog
-nbd test nbd -nbd test nbd
-ssh test ssh -ssh test ssh
-xdiff graphical mode diff -xdiff graphical mode diff
-nocache use O_DIRECT on backing file -nocache use O_DIRECT on backing file
-misalign misalign memory allocations -misalign misalign memory allocations
-n show me, do not run tests -n show me, do not run tests
-o options -o options to pass to qemu-img create/convert -o options -o options to pass to qemu-img create/convert
-T output timestamps -T output timestamps
-r randomize test order -r randomize test order
-c mode cache mode
testlist options testlist options
-g group[,group...] include tests from these groups -g group[,group...] include tests from these groups
-x group[,group...] exclude tests from these groups -x group[,group...] exclude tests from these groups
NNN include test NNN NNN include test NNN
NNN-NNN include test range (eg. 012-021) NNN-NNN include test range (eg. 012-021)
' '
exit 0 exit 0
;; ;;
...@@ -219,7 +228,8 @@ testlist options ...@@ -219,7 +228,8 @@ testlist options
xpand=false xpand=false
;; ;;
-nocache) -nocache)
QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --nocache" CACHEMODE="none"
CACHEMODE_IS_DEFAULT=false
xpand=false xpand=false
;; ;;
...@@ -258,6 +268,10 @@ testlist options ...@@ -258,6 +268,10 @@ testlist options
imgopts=true imgopts=true
xpand=false xpand=false
;; ;;
-c)
cachemode=true
xpand=false
;;
-r) # randomize test order -r) # randomize test order
randomize=true randomize=true
xpand=false xpand=false
...@@ -334,6 +348,9 @@ BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \ ...@@ -334,6 +348,9 @@ BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \
done 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 options for qemu-img create -o if they were not specified
_set_default_imgopts _set_default_imgopts
......
...@@ -157,7 +157,8 @@ _filter_qemu_io() ...@@ -157,7 +157,8 @@ _filter_qemu_io()
_filter_qemu() _filter_qemu()
{ {
sed -e "s#\\(^\\|(qemu) \\)$(basename $QEMU_PROG):#\1QEMU_PROG:#" \ 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 # replace problematic QMP output like timestamps
......
...@@ -387,25 +387,31 @@ _supported_os() ...@@ -387,25 +387,31 @@ _supported_os()
_notrun "not suitable for this OS: $HOSTOS" _notrun "not suitable for this OS: $HOSTOS"
} }
_unsupported_qemu_io_options() _supported_cache_modes()
{ {
for bad_opt for mode; do
do if [ "$mode" = "$CACHEMODE" ]; then
for opt in $QEMU_IO_OPTIONS return
do fi
if [ "$bad_opt" = "$opt" ]
then
_notrun "not suitable for qemu-io option: $bad_opt"
fi
done
done 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 # this test requires that a specified command (executable) exists
# #
_require_command() _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() _full_imgfmt_details()
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
030 rw auto backing 030 rw auto backing
031 rw auto quick 031 rw auto quick
032 rw auto 032 rw auto
033 rw auto 033 rw auto quick
034 rw auto backing 034 rw auto backing
035 rw auto quick 035 rw auto quick
036 rw auto quick 036 rw auto quick
...@@ -64,6 +64,7 @@ ...@@ -64,6 +64,7 @@
055 rw auto 055 rw auto
056 rw auto backing 056 rw auto backing
057 rw auto 057 rw auto
058 rw auto
059 rw auto 059 rw auto
060 rw auto 060 rw auto
061 rw auto 061 rw auto
...@@ -77,3 +78,4 @@ ...@@ -77,3 +78,4 @@
069 rw auto 069 rw auto
070 rw auto 070 rw auto
073 rw auto 073 rw auto
074 rw auto
...@@ -37,6 +37,7 @@ qemu_args = os.environ.get('QEMU', 'qemu').strip().split(' ') ...@@ -37,6 +37,7 @@ qemu_args = os.environ.get('QEMU', 'qemu').strip().split(' ')
imgfmt = os.environ.get('IMGFMT', 'raw') imgfmt = os.environ.get('IMGFMT', 'raw')
imgproto = os.environ.get('IMGPROTO', 'file') imgproto = os.environ.get('IMGPROTO', 'file')
test_dir = os.environ.get('TEST_DIR', '/var/tmp') 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') socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper')
...@@ -96,7 +97,7 @@ class VM(object): ...@@ -96,7 +97,7 @@ class VM(object):
'''Add a virtio-blk drive to the VM''' '''Add a virtio-blk drive to the VM'''
options = ['if=virtio', options = ['if=virtio',
'format=%s' % imgfmt, 'format=%s' % imgfmt,
'cache=none', 'cache=%s' % cachemode,
'file=%s' % path, 'file=%s' % path,
'id=drive%d' % self._num_drives] 'id=drive%d' % self._num_drives]
if opts: if opts:
......
...@@ -195,7 +195,6 @@ static void test_bh_delete_from_cb(void) ...@@ -195,7 +195,6 @@ static void test_bh_delete_from_cb(void)
g_assert(data1.bh == NULL); g_assert(data1.bh == NULL);
g_assert(!aio_poll(ctx, false)); g_assert(!aio_poll(ctx, false));
g_assert(!aio_poll(ctx, true));
} }
static void test_bh_delete_from_cb_many(void) 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 ...@@ -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_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_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_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_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_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_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_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_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" 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" ...@@ -127,6 +128,7 @@ thread_pool_cancel(void *req, void *opaque) "req %p opaque %p"
# block/raw-win32.c # block/raw-win32.c
# block/raw-posix.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" 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 # ioport.c
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册