* [Qemu-devel] [PATCH 01/11] block: Introduce bdrv_writev_vmstate
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 01/11] block/ssh: Require libssh2 >= 1.2.8 Stefan Hajnoczi
` (20 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, Anthony Liguori, Stefan Hajnoczi
From: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block.c | 25 ++++++++++++++++++++-----
block/qcow2.c | 12 +++++++++---
block/sheepdog.c | 13 ++++++++++---
include/block/block.h | 1 +
include/block/block_int.h | 4 ++--
5 files changed, 42 insertions(+), 13 deletions(-)
diff --git a/block.c b/block.c
index 602d8a4..175497e 100644
--- a/block.c
+++ b/block.c
@@ -3184,13 +3184,28 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
int64_t pos, int size)
{
+ QEMUIOVector qiov;
+ struct iovec iov = {
+ .iov_base = (void *) buf,
+ .iov_len = size,
+ };
+
+ qemu_iovec_init_external(&qiov, &iov, 1);
+ return bdrv_writev_vmstate(bs, &qiov, pos);
+}
+
+int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
+{
BlockDriver *drv = bs->drv;
- if (!drv)
+
+ if (!drv) {
return -ENOMEDIUM;
- if (drv->bdrv_save_vmstate)
- return drv->bdrv_save_vmstate(bs, buf, pos, size);
- if (bs->file)
- return bdrv_save_vmstate(bs->file, buf, pos, size);
+ } else if (drv->bdrv_save_vmstate) {
+ return drv->bdrv_save_vmstate(bs, qiov, pos);
+ } else if (bs->file) {
+ return bdrv_writev_vmstate(bs->file, qiov, pos);
+ }
+
return -ENOTSUP;
}
diff --git a/block/qcow2.c b/block/qcow2.c
index 1d18073..1d856af 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1652,18 +1652,24 @@ static void dump_refcounts(BlockDriverState *bs)
}
#endif
-static int qcow2_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
- int64_t pos, int size)
+static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
+ int64_t pos)
{
BDRVQcowState *s = bs->opaque;
int growable = bs->growable;
int ret;
+ void *buf;
+
+ buf = qemu_blockalign(bs, qiov->size);
+ qemu_iovec_to_buf(qiov, 0, buf, qiov->size);
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE);
bs->growable = 1;
- ret = bdrv_pwrite(bs, qcow2_vm_state_offset(s) + pos, buf, size);
+ ret = bdrv_pwrite(bs, qcow2_vm_state_offset(s) + pos, buf, qiov->size);
bs->growable = growable;
+ qemu_vfree(buf);
+
return ret;
}
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 987018e..1c5b532 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -2054,12 +2054,19 @@ cleanup:
return ret;
}
-static int sd_save_vmstate(BlockDriverState *bs, const uint8_t *data,
- int64_t pos, int size)
+static int sd_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
+ int64_t pos)
{
BDRVSheepdogState *s = bs->opaque;
+ void *buf;
+ int ret;
- return do_load_save_vmstate(s, (uint8_t *)data, pos, size, 0);
+ buf = qemu_blockalign(bs, qiov->size);
+ qemu_iovec_to_buf(qiov, 0, buf, qiov->size);
+ ret = do_load_save_vmstate(s, (uint8_t *) buf, pos, qiov->size, 0);
+ qemu_vfree(buf);
+
+ return ret;
}
static int sd_load_vmstate(BlockDriverState *bs, uint8_t *data,
diff --git a/include/block/block.h b/include/block/block.h
index 9dc6aad..e5fc566 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -348,6 +348,7 @@ void path_combine(char *dest, int dest_size,
const char *base_path,
const char *filename);
+int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
int64_t pos, int size);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 9aa98b5..458cde3 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -164,8 +164,8 @@ struct BlockDriver {
const char *snapshot_name);
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
- int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf,
- int64_t pos, int size);
+ int (*bdrv_save_vmstate)(BlockDriverState *bs, QEMUIOVector *qiov,
+ int64_t pos);
int (*bdrv_load_vmstate)(BlockDriverState *bs, uint8_t *buf,
int64_t pos, int size);
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 01/11] block/ssh: Require libssh2 >= 1.2.8.
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 01/11] block: Introduce bdrv_writev_vmstate Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 02/11] savevm: Implement block_writev_buffer() Stefan Hajnoczi
` (19 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Anthony Liguori, Richard W.M. Jones, Stefan Hajnoczi
From: "Richard W.M. Jones" <rjones@redhat.com>
libssh2 >= 1.2.8 is required to enable this block device (because
that version introduced the libssh2_session_handshake call).
Change the test to use pkg-config exclusively. If the user requests
--enable-libssh2 and the minimum version is not available, then the
following error is displayed:
$ ./configure --enable-libssh2
ERROR: libssh2 >= 1.2.8 required for --enable-libssh2
If --enable-libssh2 is not specified, then the feature is silently
disabled if sufficiently new libssh2 is not available.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
configure | 24 ++++--------------------
1 file changed, 4 insertions(+), 20 deletions(-)
diff --git a/configure b/configure
index ee2e7e8..8188a7c 100755
--- a/configure
+++ b/configure
@@ -2364,35 +2364,19 @@ fi
##########################################
# libssh2 probe
+min_libssh2_version=1.2.8
if test "$libssh2" != "no" ; then
- cat > $TMPC <<EOF
-#include <stdio.h>
-#include <libssh2.h>
-#include <libssh2_sftp.h>
-int main(void) {
- LIBSSH2_SESSION *session;
- session = libssh2_session_init ();
- (void) libssh2_sftp_init (session);
- return 0;
-}
-EOF
-
- if $pkg_config libssh2 --modversion >/dev/null 2>&1; then
+ if $pkg_config --atleast-version=$min_libssh2_version libssh2 >/dev/null 2>&1
+ then
libssh2_cflags=`$pkg_config libssh2 --cflags`
libssh2_libs=`$pkg_config libssh2 --libs`
- else
- libssh2_cflags=
- libssh2_libs="-lssh2"
- fi
-
- if compile_prog "$libssh2_cflags" "$libssh2_libs" ; then
libssh2=yes
libs_tools="$libssh2_libs $libs_tools"
libs_softmmu="$libssh2_libs $libs_softmmu"
QEMU_CFLAGS="$QEMU_CFLAGS $libssh2_cflags"
else
if test "$libssh2" = "yes" ; then
- feature_not_found "libssh2"
+ error_exit "libssh2 >= $min_libssh2_version required for --enable-libssh2"
fi
libssh2=no
fi
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 02/11] savevm: Implement block_writev_buffer()
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 01/11] block: Introduce bdrv_writev_vmstate Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 01/11] block/ssh: Require libssh2 >= 1.2.8 Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 02/11] sheepdog: add discard/trim support for sheepdog Stefan Hajnoczi
` (18 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, Anthony Liguori, Stefan Hajnoczi
From: Kevin Wolf <kwolf@redhat.com>
Instead of breaking up RAM state into many small chunks, pass the iovec
to the block layer for better performance.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
include/migration/qemu-file.h | 2 +-
savevm.c | 25 +++++++++++++++++++++----
2 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index 623c434..7519464 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -55,7 +55,7 @@ typedef int (QEMUFileGetFD)(void *opaque);
* This function writes an iovec to file.
*/
typedef ssize_t (QEMUFileWritevBufferFunc)(void *opaque, struct iovec *iov,
- int iovcnt);
+ int iovcnt, int64_t pos);
typedef struct QEMUFileOps {
QEMUFilePutBufferFunc *put_buffer;
diff --git a/savevm.c b/savevm.c
index b1d8988..88fe985 100644
--- a/savevm.c
+++ b/savevm.c
@@ -176,7 +176,8 @@ static void coroutine_fn yield_until_fd_readable(int fd)
qemu_coroutine_yield();
}
-static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt)
+static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
+ int64_t pos)
{
QEMUFileSocket *s = opaque;
ssize_t len;
@@ -458,6 +459,21 @@ fail:
return NULL;
}
+static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
+ int64_t pos)
+{
+ int ret;
+ QEMUIOVector qiov;
+
+ qemu_iovec_init_external(&qiov, iov, iovcnt);
+ ret = bdrv_writev_vmstate(opaque, &qiov, pos);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return qiov.size;
+}
+
static int block_put_buffer(void *opaque, const uint8_t *buf,
int64_t pos, int size)
{
@@ -481,8 +497,9 @@ static const QEMUFileOps bdrv_read_ops = {
};
static const QEMUFileOps bdrv_write_ops = {
- .put_buffer = block_put_buffer,
- .close = bdrv_fclose
+ .put_buffer = block_put_buffer,
+ .writev_buffer = block_writev_buffer,
+ .close = bdrv_fclose
};
static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable)
@@ -533,7 +550,7 @@ static void qemu_fflush(QEMUFile *f)
if (f->is_write && f->iovcnt > 0) {
if (f->ops->writev_buffer) {
- ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt);
+ ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos);
if (ret >= 0) {
f->pos += ret;
}
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 02/11] sheepdog: add discard/trim support for sheepdog
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (2 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 02/11] savevm: Implement block_writev_buffer() Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 03/11] block: Introduce bdrv_pwritev() for qcow2_save_vmstate Stefan Hajnoczi
` (17 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Anthony Liguori, Liu Yuan, Stefan Hajnoczi,
Paolo Bonzini, MORITA Kazutaka
From: Liu Yuan <tailai.ly@taobao.com>
The 'TRIM' command from VM that is to release underlying data storage for
better thin-provision is already supported by the Sheepdog.
This patch adds the TRIM support at QEMU part.
For older Sheepdog that doesn't support it, we return 0(success) to upper layer.
Cc: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
Cc: Kevin Wolf <kwolf@redhat.com>
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Liu Yuan <tailai.ly@taobao.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/sheepdog.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 63 insertions(+), 1 deletion(-)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 20b5d06..6a72c1e 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -27,6 +27,8 @@
#define SD_OP_CREATE_AND_WRITE_OBJ 0x01
#define SD_OP_READ_OBJ 0x02
#define SD_OP_WRITE_OBJ 0x03
+/* 0x04 is used internally by Sheepdog */
+#define SD_OP_DISCARD_OBJ 0x05
#define SD_OP_NEW_VDI 0x11
#define SD_OP_LOCK_VDI 0x12
@@ -269,6 +271,7 @@ enum AIOCBState {
AIOCB_WRITE_UDATA,
AIOCB_READ_UDATA,
AIOCB_FLUSH_CACHE,
+ AIOCB_DISCARD_OBJ,
};
struct SheepdogAIOCB {
@@ -298,6 +301,7 @@ typedef struct BDRVSheepdogState {
char name[SD_MAX_VDI_LEN];
bool is_snapshot;
uint32_t cache_flags;
+ bool discard_supported;
char *host_spec;
bool is_unix;
@@ -656,7 +660,7 @@ static void coroutine_fn aio_read_response(void *opaque)
int ret;
AIOReq *aio_req = NULL;
SheepdogAIOCB *acb;
- unsigned long idx;
+ uint64_t idx;
if (QLIST_EMPTY(&s->inflight_aio_head)) {
goto out;
@@ -727,6 +731,21 @@ static void coroutine_fn aio_read_response(void *opaque)
rsp.result = SD_RES_SUCCESS;
}
break;
+ case AIOCB_DISCARD_OBJ:
+ switch (rsp.result) {
+ case SD_RES_INVALID_PARMS:
+ error_report("sheep(%s) doesn't support discard command",
+ s->host_spec);
+ rsp.result = SD_RES_SUCCESS;
+ s->discard_supported = false;
+ break;
+ case SD_RES_SUCCESS:
+ idx = data_oid_to_idx(aio_req->oid);
+ s->inode.data_vdi_id[idx] = 0;
+ break;
+ default:
+ break;
+ }
}
if (rsp.result != SD_RES_SUCCESS) {
@@ -1016,6 +1035,9 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
wlen = datalen;
hdr.flags = SD_FLAG_CMD_WRITE | flags;
break;
+ case AIOCB_DISCARD_OBJ:
+ hdr.opcode = SD_OP_DISCARD_OBJ;
+ break;
}
if (s->cache_flags) {
@@ -1197,6 +1219,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags)
if (flags & BDRV_O_NOCACHE) {
s->cache_flags = SD_FLAG_CMD_DIRECT;
}
+ s->discard_supported = true;
if (snapid || tag[0] != '\0') {
dprintf("%" PRIx32 " snapshot inode was open.\n", vid);
@@ -1662,6 +1685,15 @@ static int coroutine_fn sd_co_rw_vector(void *p)
flags = SD_FLAG_CMD_COW;
}
break;
+ case AIOCB_DISCARD_OBJ:
+ /*
+ * We discard the object only when the whole object is
+ * 1) allocated 2) trimmed. Otherwise, simply skip it.
+ */
+ if (len != SD_DATA_OBJ_SIZE || inode->data_vdi_id[idx] == 0) {
+ goto done;
+ }
+ break;
default:
break;
}
@@ -2107,6 +2139,33 @@ static int sd_load_vmstate(BlockDriverState *bs, uint8_t *data,
}
+static coroutine_fn int sd_co_discard(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors)
+{
+ SheepdogAIOCB *acb;
+ QEMUIOVector dummy;
+ BDRVSheepdogState *s = bs->opaque;
+ int ret;
+
+ if (!s->discard_supported) {
+ return 0;
+ }
+
+ acb = sd_aio_setup(bs, &dummy, sector_num, nb_sectors);
+ acb->aiocb_type = AIOCB_DISCARD_OBJ;
+ acb->aio_done_func = sd_finish_aiocb;
+
+ ret = sd_co_rw_vector(acb);
+ if (ret <= 0) {
+ qemu_aio_release(acb);
+ return ret;
+ }
+
+ qemu_coroutine_yield();
+
+ return acb->ret;
+}
+
static QEMUOptionParameter sd_create_options[] = {
{
.name = BLOCK_OPT_SIZE,
@@ -2139,6 +2198,7 @@ static BlockDriver bdrv_sheepdog = {
.bdrv_co_readv = sd_co_readv,
.bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
+ .bdrv_co_discard = sd_co_discard,
.bdrv_snapshot_create = sd_snapshot_create,
.bdrv_snapshot_goto = sd_snapshot_goto,
@@ -2164,6 +2224,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
.bdrv_co_readv = sd_co_readv,
.bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
+ .bdrv_co_discard = sd_co_discard,
.bdrv_snapshot_create = sd_snapshot_create,
.bdrv_snapshot_goto = sd_snapshot_goto,
@@ -2189,6 +2250,7 @@ static BlockDriver bdrv_sheepdog_unix = {
.bdrv_co_readv = sd_co_readv,
.bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
+ .bdrv_co_discard = sd_co_discard,
.bdrv_snapshot_create = sd_snapshot_create,
.bdrv_snapshot_goto = sd_snapshot_goto,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 03/11] block: Introduce bdrv_pwritev() for qcow2_save_vmstate
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (3 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 02/11] sheepdog: add discard/trim support for sheepdog Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 03/11] sheepdog: use BDRV_SECTOR_SIZE Stefan Hajnoczi
` (16 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, Anthony Liguori, Stefan Hajnoczi
From: Kevin Wolf <kwolf@redhat.com>
Directly pass the QEMUIOVector on instead of linearising it.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block.c | 80 +++++++++++++++++++++++++++++++++++++--------------
block/qcow2.c | 8 +-----
include/block/block.h | 2 ++
3 files changed, 61 insertions(+), 29 deletions(-)
diff --git a/block.c b/block.c
index 175497e..4ad663d 100644
--- a/block.c
+++ b/block.c
@@ -2131,27 +2131,21 @@ static void coroutine_fn bdrv_rw_co_entry(void *opaque)
}
/*
- * Process a synchronous request using coroutines
+ * Process a vectored synchronous request using coroutines
*/
-static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf,
- int nb_sectors, bool is_write)
+static int bdrv_rwv_co(BlockDriverState *bs, int64_t sector_num,
+ QEMUIOVector *qiov, bool is_write)
{
- QEMUIOVector qiov;
- struct iovec iov = {
- .iov_base = (void *)buf,
- .iov_len = nb_sectors * BDRV_SECTOR_SIZE,
- };
Coroutine *co;
RwCo rwco = {
.bs = bs,
.sector_num = sector_num,
- .nb_sectors = nb_sectors,
- .qiov = &qiov,
+ .nb_sectors = qiov->size >> BDRV_SECTOR_BITS,
+ .qiov = qiov,
.is_write = is_write,
.ret = NOT_DONE,
};
-
- qemu_iovec_init_external(&qiov, &iov, 1);
+ assert((qiov->size & (BDRV_SECTOR_SIZE - 1)) == 0);
/**
* In sync call context, when the vcpu is blocked, this throttling timer
@@ -2177,6 +2171,22 @@ static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf,
return rwco.ret;
}
+/*
+ * Process a synchronous request using coroutines
+ */
+static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf,
+ int nb_sectors, bool is_write)
+{
+ QEMUIOVector qiov;
+ struct iovec iov = {
+ .iov_base = (void *)buf,
+ .iov_len = nb_sectors * BDRV_SECTOR_SIZE,
+ };
+
+ qemu_iovec_init_external(&qiov, &iov, 1);
+ return bdrv_rwv_co(bs, sector_num, &qiov, is_write);
+}
+
/* return < 0 if error. See bdrv_write() for the return codes */
int bdrv_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
@@ -2210,6 +2220,11 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num,
return bdrv_rw_co(bs, sector_num, (uint8_t *)buf, nb_sectors, true);
}
+int bdrv_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov)
+{
+ return bdrv_rwv_co(bs, sector_num, qiov, true);
+}
+
int bdrv_pread(BlockDriverState *bs, int64_t offset,
void *buf, int count1)
{
@@ -2255,15 +2270,15 @@ int bdrv_pread(BlockDriverState *bs, int64_t offset,
return count1;
}
-int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
- const void *buf, int count1)
+int bdrv_pwritev(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov)
{
uint8_t tmp_buf[BDRV_SECTOR_SIZE];
int len, nb_sectors, count;
int64_t sector_num;
int ret;
- count = count1;
+ count = qiov->size;
+
/* first write to align to sector start */
len = (BDRV_SECTOR_SIZE - offset) & (BDRV_SECTOR_SIZE - 1);
if (len > count)
@@ -2272,24 +2287,32 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
if (len > 0) {
if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0)
return ret;
- memcpy(tmp_buf + (offset & (BDRV_SECTOR_SIZE - 1)), buf, len);
+ qemu_iovec_to_buf(qiov, 0, tmp_buf + (offset & (BDRV_SECTOR_SIZE - 1)),
+ len);
if ((ret = bdrv_write(bs, sector_num, tmp_buf, 1)) < 0)
return ret;
count -= len;
if (count == 0)
- return count1;
+ return qiov->size;
sector_num++;
- buf += len;
}
/* write the sectors "in place" */
nb_sectors = count >> BDRV_SECTOR_BITS;
if (nb_sectors > 0) {
- if ((ret = bdrv_write(bs, sector_num, buf, nb_sectors)) < 0)
+ QEMUIOVector qiov_inplace;
+
+ qemu_iovec_init(&qiov_inplace, qiov->niov);
+ qemu_iovec_concat(&qiov_inplace, qiov, len,
+ nb_sectors << BDRV_SECTOR_BITS);
+ ret = bdrv_writev(bs, sector_num, &qiov_inplace);
+ qemu_iovec_destroy(&qiov_inplace);
+ if (ret < 0) {
return ret;
+ }
+
sector_num += nb_sectors;
len = nb_sectors << BDRV_SECTOR_BITS;
- buf += len;
count -= len;
}
@@ -2297,11 +2320,24 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
if (count > 0) {
if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0)
return ret;
- memcpy(tmp_buf, buf, count);
+ qemu_iovec_to_buf(qiov, qiov->size - count, tmp_buf, count);
if ((ret = bdrv_write(bs, sector_num, tmp_buf, 1)) < 0)
return ret;
}
- return count1;
+ return qiov->size;
+}
+
+int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
+ const void *buf, int count1)
+{
+ QEMUIOVector qiov;
+ struct iovec iov = {
+ .iov_base = (void *) buf,
+ .iov_len = count1,
+ };
+
+ qemu_iovec_init_external(&qiov, &iov, 1);
+ return bdrv_pwritev(bs, offset, &qiov);
}
/*
diff --git a/block/qcow2.c b/block/qcow2.c
index 1d856af..e8934de 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1658,18 +1658,12 @@ static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
BDRVQcowState *s = bs->opaque;
int growable = bs->growable;
int ret;
- void *buf;
-
- buf = qemu_blockalign(bs, qiov->size);
- qemu_iovec_to_buf(qiov, 0, buf, qiov->size);
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE);
bs->growable = 1;
- ret = bdrv_pwrite(bs, qcow2_vm_state_offset(s) + pos, buf, qiov->size);
+ ret = bdrv_pwritev(bs, qcow2_vm_state_offset(s) + pos, qiov);
bs->growable = growable;
- qemu_vfree(buf);
-
return ret;
}
diff --git a/include/block/block.h b/include/block/block.h
index e5fc566..ebd9512 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -166,10 +166,12 @@ int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors);
int bdrv_write(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
+int bdrv_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov);
int bdrv_pread(BlockDriverState *bs, int64_t offset,
void *buf, int count);
int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
const void *buf, int count);
+int bdrv_pwritev(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov);
int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset,
const void *buf, int count);
int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 03/11] sheepdog: use BDRV_SECTOR_SIZE
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (4 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 03/11] block: Introduce bdrv_pwritev() for qcow2_save_vmstate Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 04/11] qemu-iotests: A few more bdrv_pread/pwrite tests Stefan Hajnoczi
` (15 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Anthony Liguori, Stefan Hajnoczi, Liu Yuan, MORITA Kazutaka
From: Liu Yuan <tailai.ly@taobao.com>
Cc: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
Cc: Kevin Wolf <kwolf@redhat.com>
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Liu Yuan <tailai.ly@taobao.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/sheepdog.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 6a72c1e..2772e8e 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -89,7 +89,6 @@
#define SD_NR_VDIS (1U << 24)
#define SD_DATA_OBJ_SIZE (UINT64_C(1) << 22)
#define SD_MAX_VDI_SIZE (SD_DATA_OBJ_SIZE * MAX_DATA_OBJS)
-#define SECTOR_SIZE 512
#define SD_INODE_SIZE (sizeof(SheepdogInode))
#define CURRENT_VDI_ID 0
@@ -1246,7 +1245,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags)
s->min_dirty_data_idx = UINT32_MAX;
s->max_dirty_data_idx = 0;
- bs->total_sectors = s->inode.vdi_size / SECTOR_SIZE;
+ bs->total_sectors = s->inode.vdi_size / BDRV_SECTOR_SIZE;
pstrcpy(s->name, sizeof(s->name), vdi);
qemu_co_mutex_init(&s->lock);
qemu_opts_del(opts);
@@ -1633,10 +1632,10 @@ static int coroutine_fn sd_co_rw_vector(void *p)
{
SheepdogAIOCB *acb = p;
int ret = 0;
- unsigned long len, done = 0, total = acb->nb_sectors * SECTOR_SIZE;
- unsigned long idx = acb->sector_num * SECTOR_SIZE / SD_DATA_OBJ_SIZE;
+ unsigned long len, done = 0, total = acb->nb_sectors * BDRV_SECTOR_SIZE;
+ unsigned long idx = acb->sector_num * BDRV_SECTOR_SIZE / SD_DATA_OBJ_SIZE;
uint64_t oid;
- uint64_t offset = (acb->sector_num * SECTOR_SIZE) % SD_DATA_OBJ_SIZE;
+ uint64_t offset = (acb->sector_num * BDRV_SECTOR_SIZE) % SD_DATA_OBJ_SIZE;
BDRVSheepdogState *s = acb->common.bs->opaque;
SheepdogInode *inode = &s->inode;
AIOReq *aio_req;
@@ -1755,7 +1754,7 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
int ret;
if (bs->growable && sector_num + nb_sectors > bs->total_sectors) {
- ret = sd_truncate(bs, (sector_num + nb_sectors) * SECTOR_SIZE);
+ ret = sd_truncate(bs, (sector_num + nb_sectors) * BDRV_SECTOR_SIZE);
if (ret < 0) {
return ret;
}
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 04/11] qemu-iotests: A few more bdrv_pread/pwrite tests
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (5 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 03/11] sheepdog: use BDRV_SECTOR_SIZE Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 04/11] sheepdog: implement .bdrv_co_is_allocated() Stefan Hajnoczi
` (14 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, Anthony Liguori, Stefan Hajnoczi
From: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
tests/qemu-iotests/002 | 13 +++++++++++++
tests/qemu-iotests/002.out | 26 ++++++++++++++++++++++++++
2 files changed, 39 insertions(+)
diff --git a/tests/qemu-iotests/002 b/tests/qemu-iotests/002
index bebed84..51d0a8f 100755
--- a/tests/qemu-iotests/002
+++ b/tests/qemu-iotests/002
@@ -61,10 +61,23 @@ $QEMU_IO -c "read -pP 0xa 0 $size" $TEST_IMG | _filter_qemu_io
echo
echo "unaligned pwrite"
$QEMU_IO -c 'write -pP 0xab 66 42' $TEST_IMG | _filter_qemu_io
+$QEMU_IO -c 'write -pP 0xac 512 288' $TEST_IMG | _filter_qemu_io
+$QEMU_IO -c 'write -pP 0xad 800 224' $TEST_IMG | _filter_qemu_io
+$QEMU_IO -c 'write -pP 0xae 66000 128k' $TEST_IMG | _filter_qemu_io
+$QEMU_IO -c 'write -pP 0xaf 256k 42' $TEST_IMG | _filter_qemu_io
echo
echo "verify pattern"
+$QEMU_IO -c 'read -pP 0xa 0 66' $TEST_IMG | _filter_qemu_io
$QEMU_IO -c 'read -pP 0xab 66 42' $TEST_IMG | _filter_qemu_io
+$QEMU_IO -c 'read -pP 0xa 108 404' $TEST_IMG | _filter_qemu_io
+$QEMU_IO -c 'read -pP 0xac 512 288' $TEST_IMG | _filter_qemu_io
+$QEMU_IO -c 'read -pP 0xad 800 224' $TEST_IMG | _filter_qemu_io
+$QEMU_IO -c 'read -pP 0xa 1k 64976' $TEST_IMG | _filter_qemu_io
+$QEMU_IO -c 'read -pP 0xae 66000 128k' $TEST_IMG | _filter_qemu_io
+$QEMU_IO -c 'read -pP 0xa 197072 65072' $TEST_IMG | _filter_qemu_io
+$QEMU_IO -c 'read -pP 0xaf 256k 42' $TEST_IMG | _filter_qemu_io
+$QEMU_IO -c 'read -pP 0xa 262186 470' $TEST_IMG | _filter_qemu_io
# success, all done
echo "*** done"
diff --git a/tests/qemu-iotests/002.out b/tests/qemu-iotests/002.out
index 75f5876..cd6aa0f 100644
--- a/tests/qemu-iotests/002.out
+++ b/tests/qemu-iotests/002.out
@@ -16,8 +16,34 @@ read 134217728/134217728 bytes at offset 0
unaligned pwrite
wrote 42/42 bytes at offset 66
42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 288/288 bytes at offset 512
+288 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 224/224 bytes at offset 800
+224 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 66000
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 42/42 bytes at offset 262144
+42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
verify pattern
+read 66/66 bytes at offset 0
+66 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 42/42 bytes at offset 66
42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 404/404 bytes at offset 108
+404 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 288/288 bytes at offset 512
+288 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 224/224 bytes at offset 800
+224 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 64976/64976 bytes at offset 1024
+63.453 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 131072/131072 bytes at offset 66000
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65072/65072 bytes at offset 197072
+63.547 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 42/42 bytes at offset 262144
+42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 470/470 bytes at offset 262186
+470 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 04/11] sheepdog: implement .bdrv_co_is_allocated()
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (6 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 04/11] qemu-iotests: A few more bdrv_pread/pwrite tests Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 05/11] block: Disable driver-specific options for 1.5 Stefan Hajnoczi
` (13 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Anthony Liguori, Stefan Hajnoczi, Liu Yuan, MORITA Kazutaka
From: Liu Yuan <tailai.ly@taobao.com>
Cc: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
Cc: Kevin Wolf <kwolf@redhat.com>
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Liu Yuan <tailai.ly@taobao.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/sheepdog.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 2772e8e..9f30a87 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -2165,6 +2165,40 @@ static coroutine_fn int sd_co_discard(BlockDriverState *bs, int64_t sector_num,
return acb->ret;
}
+static coroutine_fn int
+sd_co_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
+ int *pnum)
+{
+ BDRVSheepdogState *s = bs->opaque;
+ SheepdogInode *inode = &s->inode;
+ unsigned long start = sector_num * BDRV_SECTOR_SIZE / SD_DATA_OBJ_SIZE,
+ end = DIV_ROUND_UP((sector_num + nb_sectors) *
+ BDRV_SECTOR_SIZE, SD_DATA_OBJ_SIZE);
+ unsigned long idx;
+ int ret = 1;
+
+ for (idx = start; idx < end; idx++) {
+ if (inode->data_vdi_id[idx] == 0) {
+ break;
+ }
+ }
+ if (idx == start) {
+ /* Get the longest length of unallocated sectors */
+ ret = 0;
+ for (idx = start + 1; idx < end; idx++) {
+ if (inode->data_vdi_id[idx] != 0) {
+ break;
+ }
+ }
+ }
+
+ *pnum = (idx - start) * SD_DATA_OBJ_SIZE / BDRV_SECTOR_SIZE;
+ if (*pnum > nb_sectors) {
+ *pnum = nb_sectors;
+ }
+ return ret;
+}
+
static QEMUOptionParameter sd_create_options[] = {
{
.name = BLOCK_OPT_SIZE,
@@ -2198,6 +2232,7 @@ static BlockDriver bdrv_sheepdog = {
.bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
.bdrv_co_discard = sd_co_discard,
+ .bdrv_co_is_allocated = sd_co_is_allocated,
.bdrv_snapshot_create = sd_snapshot_create,
.bdrv_snapshot_goto = sd_snapshot_goto,
@@ -2224,6 +2259,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
.bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
.bdrv_co_discard = sd_co_discard,
+ .bdrv_co_is_allocated = sd_co_is_allocated,
.bdrv_snapshot_create = sd_snapshot_create,
.bdrv_snapshot_goto = sd_snapshot_goto,
@@ -2250,6 +2286,7 @@ static BlockDriver bdrv_sheepdog_unix = {
.bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
.bdrv_co_discard = sd_co_discard,
+ .bdrv_co_is_allocated = sd_co_is_allocated,
.bdrv_snapshot_create = sd_snapshot_create,
.bdrv_snapshot_goto = sd_snapshot_goto,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 05/11] block: Disable driver-specific options for 1.5
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (7 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 04/11] sheepdog: implement .bdrv_co_is_allocated() Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 05/11] qemu-iotests: Add test for -drive options Stefan Hajnoczi
` (12 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, Anthony Liguori, Stefan Hajnoczi
From: Kevin Wolf <kwolf@redhat.com>
We don't want to commit to the API yet before everything is worked out.
Disable it for the 1.5 release. This commit is meant to be reverted
after the 1.5 release.
The disabling of the driver-specific options is achieved by applying the
old checks while parsing the command line.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
blockdev.c | 118 +++++++++++++++++++++++++++++++++++++++++++++--
tests/qemu-iotests/group | 2 +-
2 files changed, 115 insertions(+), 5 deletions(-)
diff --git a/blockdev.c b/blockdev.c
index 8a1652b..6e293e9 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1656,10 +1656,120 @@ QemuOptsList qemu_drive_opts = {
.name = "drive",
.head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head),
.desc = {
- /*
- * no elements => accept any params
- * validation will happen later
- */
+ {
+ .name = "bus",
+ .type = QEMU_OPT_NUMBER,
+ .help = "bus number",
+ },{
+ .name = "unit",
+ .type = QEMU_OPT_NUMBER,
+ .help = "unit number (i.e. lun for scsi)",
+ },{
+ .name = "if",
+ .type = QEMU_OPT_STRING,
+ .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
+ },{
+ .name = "index",
+ .type = QEMU_OPT_NUMBER,
+ .help = "index number",
+ },{
+ .name = "cyls",
+ .type = QEMU_OPT_NUMBER,
+ .help = "number of cylinders (ide disk geometry)",
+ },{
+ .name = "heads",
+ .type = QEMU_OPT_NUMBER,
+ .help = "number of heads (ide disk geometry)",
+ },{
+ .name = "secs",
+ .type = QEMU_OPT_NUMBER,
+ .help = "number of sectors (ide disk geometry)",
+ },{
+ .name = "trans",
+ .type = QEMU_OPT_STRING,
+ .help = "chs translation (auto, lba. none)",
+ },{
+ .name = "media",
+ .type = QEMU_OPT_STRING,
+ .help = "media type (disk, cdrom)",
+ },{
+ .name = "snapshot",
+ .type = QEMU_OPT_BOOL,
+ .help = "enable/disable snapshot mode",
+ },{
+ .name = "file",
+ .type = QEMU_OPT_STRING,
+ .help = "disk image",
+ },{
+ .name = "discard",
+ .type = QEMU_OPT_STRING,
+ .help = "discard operation (ignore/off, unmap/on)",
+ },{
+ .name = "cache",
+ .type = QEMU_OPT_STRING,
+ .help = "host cache usage (none, writeback, writethrough, "
+ "directsync, unsafe)",
+ },{
+ .name = "aio",
+ .type = QEMU_OPT_STRING,
+ .help = "host AIO implementation (threads, native)",
+ },{
+ .name = "format",
+ .type = QEMU_OPT_STRING,
+ .help = "disk format (raw, qcow2, ...)",
+ },{
+ .name = "serial",
+ .type = QEMU_OPT_STRING,
+ .help = "disk serial number",
+ },{
+ .name = "rerror",
+ .type = QEMU_OPT_STRING,
+ .help = "read error action",
+ },{
+ .name = "werror",
+ .type = QEMU_OPT_STRING,
+ .help = "write error action",
+ },{
+ .name = "addr",
+ .type = QEMU_OPT_STRING,
+ .help = "pci address (virtio only)",
+ },{
+ .name = "readonly",
+ .type = QEMU_OPT_BOOL,
+ .help = "open drive file as read-only",
+ },{
+ .name = "iops",
+ .type = QEMU_OPT_NUMBER,
+ .help = "limit total I/O operations per second",
+ },{
+ .name = "iops_rd",
+ .type = QEMU_OPT_NUMBER,
+ .help = "limit read operations per second",
+ },{
+ .name = "iops_wr",
+ .type = QEMU_OPT_NUMBER,
+ .help = "limit write operations per second",
+ },{
+ .name = "bps",
+ .type = QEMU_OPT_NUMBER,
+ .help = "limit total bytes per second",
+ },{
+ .name = "bps_rd",
+ .type = QEMU_OPT_NUMBER,
+ .help = "limit read bytes per second",
+ },{
+ .name = "bps_wr",
+ .type = QEMU_OPT_NUMBER,
+ .help = "limit write bytes per second",
+ },{
+ .name = "copy-on-read",
+ .type = QEMU_OPT_BOOL,
+ .help = "copy read data from backing file into image file",
+ },{
+ .name = "boot",
+ .type = QEMU_OPT_BOOL,
+ .help = "(deprecated, ignored)",
+ },
{ /* end of list */ }
},
};
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 68eabda..bf944d9 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -57,6 +57,6 @@
048 img auto quick
049 rw auto
050 rw auto backing quick
-051 rw auto
+#051 rw auto
052 rw auto backing
053 rw auto
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 05/11] qemu-iotests: Add test for -drive options
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (8 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 05/11] block: Disable driver-specific options for 1.5 Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 06/11] qemu-iotests: filter QEMU_PROG in 051.out Stefan Hajnoczi
` (11 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, Anthony Liguori, Stefan Hajnoczi
From: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
tests/qemu-iotests/051 | 148 +++++++++++++++++++++++++++++++++++++++++
tests/qemu-iotests/051.out | 162 +++++++++++++++++++++++++++++++++++++++++++++
tests/qemu-iotests/group | 1 +
3 files changed, 311 insertions(+)
create mode 100755 tests/qemu-iotests/051
create mode 100644 tests/qemu-iotests/051.out
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
new file mode 100755
index 0000000..17a1d92
--- /dev/null
+++ b/tests/qemu-iotests/051
@@ -0,0 +1,148 @@
+#!/bin/bash
+#
+# Test command line configuration of block devices and driver-specific options
+#
+# 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=kwolf@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+function do_run_qemu()
+{
+ echo Testing: "$@"
+ echo quit | $QEMU -nographic -monitor stdio -serial none "$@"
+ echo
+}
+
+function run_qemu()
+{
+ do_run_qemu "$@" 2>&1 | _filter_testdir
+}
+
+size=128M
+
+_make_test_img $size
+
+echo
+echo === Unknown option ===
+echo
+
+run_qemu -drive file=$TEST_IMG,format=qcow2,unknown_opt=
+run_qemu -drive file=$TEST_IMG,format=qcow2,unknown_opt=on
+run_qemu -drive file=$TEST_IMG,format=qcow2,unknown_opt=1234
+run_qemu -drive file=$TEST_IMG,format=qcow2,unknown_opt=foo
+
+
+echo
+echo === Enable and disable lazy refcounting on the command line, plus some invalid values ===
+echo
+
+run_qemu -drive file=$TEST_IMG,format=qcow2,lazy_refcounts=on
+run_qemu -drive file=$TEST_IMG,format=qcow2,lazy_refcounts=off
+run_qemu -drive file=$TEST_IMG,format=qcow2,lazy_refcounts=
+run_qemu -drive file=$TEST_IMG,format=qcow2,lazy_refcounts=42
+run_qemu -drive file=$TEST_IMG,format=qcow2,lazy_refcounts=foo
+
+
+echo
+echo === With version 2 images enabling lazy refcounts must fail ===
+echo
+
+_make_test_img -ocompat=0.10 $size
+
+run_qemu -drive file=$TEST_IMG,format=qcow2,lazy_refcounts=on
+run_qemu -drive file=$TEST_IMG,format=qcow2,lazy_refcounts=off
+
+echo
+echo === No medium ===
+echo
+
+run_qemu -drive if=floppy
+run_qemu -drive if=ide,media=cdrom
+run_qemu -drive if=scsi,media=cdrom
+
+run_qemu -drive if=ide
+run_qemu -drive if=virtio
+run_qemu -drive if=scsi
+
+run_qemu -drive if=none,id=disk -device ide-cd,drive=disk
+run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk
+
+run_qemu -drive if=none,id=disk -device ide-drive,drive=disk
+run_qemu -drive if=none,id=disk -device ide-hd,drive=disk
+run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-disk,drive=disk
+run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-hd,drive=disk
+
+echo
+echo === Read-only ===
+echo
+
+run_qemu -drive file=$TEST_IMG,if=floppy,readonly=on
+run_qemu -drive file=$TEST_IMG,if=ide,media=cdrom,readonly=on
+run_qemu -drive file=$TEST_IMG,if=scsi,media=cdrom,readonly=on
+
+run_qemu -drive file=$TEST_IMG,if=ide,readonly=on
+run_qemu -drive file=$TEST_IMG,if=virtio,readonly=on
+run_qemu -drive file=$TEST_IMG,if=scsi,readonly=on
+
+run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device ide-cd,drive=disk
+run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk
+
+run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device ide-drive,drive=disk
+run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device ide-hd,drive=disk
+run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk
+run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk
+
+echo
+echo === Cache modes ===
+echo
+
+# Cannot use the test image because cache=none might not work on the host FS
+# Use cdrom so that we won't get errors about missing media
+
+run_qemu -drive media=cdrom,cache=none
+run_qemu -drive media=cdrom,cache=directsync
+run_qemu -drive media=cdrom,cache=writeback
+run_qemu -drive media=cdrom,cache=writethrough
+run_qemu -drive media=cdrom,cache=unsafe
+run_qemu -drive media=cdrom,cache=invalid_value
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
new file mode 100644
index 0000000..c206186
--- /dev/null
+++ b/tests/qemu-iotests/051.out
@@ -0,0 +1,162 @@
+QA output created by 051
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+
+=== Unknown option ===
+
+Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+
+Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+
+Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+
+Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+
+
+=== 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
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=off
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=: Parameter 'lazy_refcounts' expects 'on' or 'off'
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+
+Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=42
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=42: Parameter 'lazy_refcounts' expects 'on' or 'off'
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=42: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+
+Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=foo
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=foo: Parameter 'lazy_refcounts' expects 'on' or 'off'
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=foo: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+
+
+=== With version 2 images enabling lazy refcounts must fail ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=on
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=on: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level
+qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=on: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+
+Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=off
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+
+=== No medium ===
+
+Testing: -drive if=floppy
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive if=ide,media=cdrom
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive if=scsi,media=cdrom
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive if=ide
+qemu: Device needs media, but drive is empty
+qemu: Initialization of device ide-hd failed
+
+Testing: -drive if=virtio
+qemu: -drive if=virtio: Device needs media, but drive is empty
+qemu: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized
+
+Testing: -drive if=scsi
+qemu: -drive if=scsi: Device needs media, but drive is empty
+qemu: Initialization of device lsi53c895a failed
+
+Testing: -drive if=none,id=disk -device ide-cd,drive=disk
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive if=none,id=disk -device ide-drive,drive=disk
+qemu: -device ide-drive,drive=disk: Device needs media, but drive is empty
+qemu: -device ide-drive,drive=disk: Device 'ide-drive' could not be initialized
+
+Testing: -drive if=none,id=disk -device ide-hd,drive=disk
+qemu: -device ide-hd,drive=disk: Device needs media, but drive is empty
+qemu: -device ide-hd,drive=disk: Device 'ide-hd' could not be initialized
+
+Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-disk,drive=disk
+qemu: -device scsi-disk,drive=disk: Device needs media, but drive is empty
+qemu: -device scsi-disk,drive=disk: Device 'scsi-disk' could not be initialized
+
+Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-hd,drive=disk
+qemu: -device scsi-hd,drive=disk: Device needs media, but drive is empty
+qemu: -device scsi-hd,drive=disk: Device 'scsi-hd' could not be initialized
+
+
+=== Read-only ===
+
+Testing: -drive file=TEST_DIR/t.qcow2,if=floppy,readonly=on
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive file=TEST_DIR/t.qcow2,if=ide,media=cdrom,readonly=on
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,media=cdrom,readonly=on
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on
+qemu: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on: readonly not supported by this bus type
+
+Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,readonly=on
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-cd,drive=disk
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-drive,drive=disk
+qemu: -device ide-drive,drive=disk: Can't use a read-only drive
+qemu: -device ide-drive,drive=disk: Device 'ide-drive' could not be initialized
+
+Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-hd,drive=disk
+qemu: -device ide-hd,drive=disk: Can't use a read-only drive
+qemu: -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
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+
+=== Cache modes ===
+
+Testing: -drive media=cdrom,cache=none
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive media=cdrom,cache=directsync
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive media=cdrom,cache=writeback
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive media=cdrom,cache=writethrough
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive media=cdrom,cache=unsafe
+q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
+
+Testing: -drive media=cdrom,cache=invalid_value
+qemu: -drive media=cdrom,cache=invalid_value: invalid cache option
+
+*** done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 73c5966..324bacb 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -57,4 +57,5 @@
048 img auto quick
049 rw auto
050 rw auto backing quick
+051 rw auto
052 rw auto backing
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 06/11] qemu-iotests: filter QEMU_PROG in 051.out
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (9 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 05/11] qemu-iotests: Add test for -drive options Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 06/11] rbd: Fix use after free in rbd_open() Stefan Hajnoczi
` (10 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Anthony Liguori, Stefan Hajnoczi
Filter the name of the QEMU executable so the output can be diffed no
matter what QEMU_PROG is (e.g. qemu-system-x86_64).
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
tests/qemu-iotests/051 | 2 +-
tests/qemu-iotests/051.out | 72 ++++++++++++++++++++--------------------
tests/qemu-iotests/common.filter | 6 ++++
3 files changed, 43 insertions(+), 37 deletions(-)
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
index 17a1d92..8b51de3 100755
--- a/tests/qemu-iotests/051
+++ b/tests/qemu-iotests/051
@@ -51,7 +51,7 @@ function do_run_qemu()
function run_qemu()
{
- do_run_qemu "$@" 2>&1 | _filter_testdir
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu
}
size=128M
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index c206186..48456d5 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -4,20 +4,20 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
=== Unknown option ===
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Invalid argument
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Invalid argument
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Invalid argument
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Invalid argument
=== Enable and disable lazy refcounting on the command line, plus some invalid values ===
@@ -29,24 +29,24 @@ Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=off
q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=: Parameter 'lazy_refcounts' expects 'on' or 'off'
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=: 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: Invalid argument
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=42
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=42: Parameter 'lazy_refcounts' expects 'on' or 'off'
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=42: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=42: Parameter 'lazy_refcounts' expects 'on' or 'off'
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=42: could not open disk image TEST_DIR/t.qcow2: Invalid argument
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=foo
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=foo: Parameter 'lazy_refcounts' expects 'on' or 'off'
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=foo: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=foo: Parameter 'lazy_refcounts' expects 'on' or 'off'
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=foo: could not open disk image TEST_DIR/t.qcow2: Invalid argument
=== With version 2 images enabling lazy refcounts must fail ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=on
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=on: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level
-qemu: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=on: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=on: 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: Invalid argument
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=off
q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
@@ -64,16 +64,16 @@ Testing: -drive if=scsi,media=cdrom
q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
Testing: -drive if=ide
-qemu: Device needs media, but drive is empty
-qemu: Initialization of device ide-hd failed
+QEMU_PROG: Device needs media, but drive is empty
+QEMU_PROG: Initialization of device ide-hd failed
Testing: -drive if=virtio
-qemu: -drive if=virtio: Device needs media, but drive is empty
-qemu: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized
+QEMU_PROG: -drive if=virtio: Device needs media, but drive is empty
+QEMU_PROG: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized
Testing: -drive if=scsi
-qemu: -drive if=scsi: Device needs media, but drive is empty
-qemu: Initialization of device lsi53c895a failed
+QEMU_PROG: -drive if=scsi: Device needs media, but drive is empty
+QEMU_PROG: Initialization of device lsi53c895a failed
Testing: -drive if=none,id=disk -device ide-cd,drive=disk
q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
@@ -82,20 +82,20 @@ Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk
q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
Testing: -drive if=none,id=disk -device ide-drive,drive=disk
-qemu: -device ide-drive,drive=disk: Device needs media, but drive is empty
-qemu: -device ide-drive,drive=disk: Device 'ide-drive' could not be initialized
+QEMU_PROG: -device ide-drive,drive=disk: Device needs media, but drive is empty
+QEMU_PROG: -device ide-drive,drive=disk: Device 'ide-drive' could not be initialized
Testing: -drive if=none,id=disk -device ide-hd,drive=disk
-qemu: -device ide-hd,drive=disk: Device needs media, but drive is empty
-qemu: -device ide-hd,drive=disk: Device 'ide-hd' could not be initialized
+QEMU_PROG: -device ide-hd,drive=disk: Device needs media, but drive is empty
+QEMU_PROG: -device ide-hd,drive=disk: Device 'ide-hd' could not be initialized
Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-disk,drive=disk
-qemu: -device scsi-disk,drive=disk: Device needs media, but drive is empty
-qemu: -device scsi-disk,drive=disk: Device 'scsi-disk' could not be initialized
+QEMU_PROG: -device scsi-disk,drive=disk: Device needs media, but drive is empty
+QEMU_PROG: -device scsi-disk,drive=disk: Device 'scsi-disk' could not be initialized
Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-hd,drive=disk
-qemu: -device scsi-hd,drive=disk: Device needs media, but drive is empty
-qemu: -device scsi-hd,drive=disk: Device 'scsi-hd' could not be initialized
+QEMU_PROG: -device scsi-hd,drive=disk: Device needs media, but drive is empty
+QEMU_PROG: -device scsi-hd,drive=disk: Device 'scsi-hd' could not be initialized
=== Read-only ===
@@ -110,7 +110,7 @@ Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,media=cdrom,readonly=on
q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on
-qemu: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on: readonly not supported by this bus type
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on: readonly not supported by this bus type
Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on
q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
@@ -125,12 +125,12 @@ Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c
q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-drive,drive=disk
-qemu: -device ide-drive,drive=disk: Can't use a read-only drive
-qemu: -device ide-drive,drive=disk: Device 'ide-drive' could not be initialized
+QEMU_PROG: -device ide-drive,drive=disk: Can't use a read-only drive
+QEMU_PROG: -device ide-drive,drive=disk: Device 'ide-drive' could not be initialized
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-hd,drive=disk
-qemu: -device ide-hd,drive=disk: Can't use a read-only drive
-qemu: -device ide-hd,drive=disk: Device 'ide-hd' could not be initialized
+QEMU_PROG: -device ide-hd,drive=disk: Can't use a read-only drive
+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
q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
@@ -157,6 +157,6 @@ Testing: -drive media=cdrom,cache=unsafe
q^[[K^[[Dqu^[[K^[[D^[[Dqui^[[K^[[D^[[D^[[Dquit^[[K
Testing: -drive media=cdrom,cache=invalid_value
-qemu: -drive media=cdrom,cache=invalid_value: invalid cache option
+QEMU_PROG: -drive media=cdrom,cache=invalid_value: invalid cache option
*** done
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
index fa26b62..bc5f250 100644
--- a/tests/qemu-iotests/common.filter
+++ b/tests/qemu-iotests/common.filter
@@ -152,5 +152,11 @@ _filter_qemu_io()
_filter_win32 | sed -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/"
}
+# replace occurrences of QEMU_PROG with "qemu"
+_filter_qemu()
+{
+ sed -e "s#$(basename $QEMU_PROG)#QEMU_PROG#g"
+}
+
# make sure this script returns success
/bin/true
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 06/11] rbd: Fix use after free in rbd_open()
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (10 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 06/11] qemu-iotests: filter QEMU_PROG in 051.out Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 07/11] ide: refuse WIN_READ_NATIVE_MAX on empty device Stefan Hajnoczi
` (9 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, Anthony Liguori, Stefan Hajnoczi
From: Kevin Wolf <kwolf@redhat.com>
Commit a9ccedc3 frees the QemuOpts for the driver-specific options
immediately, even though it still needs the filename string that is
contained there. This doesn't work. Move the deletion of the QemuOpts to
the end of the function where its content isn't needed any more.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/rbd.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/block/rbd.c b/block/rbd.c
index 1826411..0f2608b 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -478,20 +478,20 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags)
}
filename = qemu_opt_get(opts, "filename");
- qemu_opts_del(opts);
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
snap_buf, sizeof(snap_buf),
s->name, sizeof(s->name),
conf, sizeof(conf)) < 0) {
- return -EINVAL;
+ r = -EINVAL;
+ goto failed_opts;
}
clientname = qemu_rbd_parse_clientname(conf, clientname_buf);
r = rados_create(&s->cluster, clientname);
if (r < 0) {
error_report("error initializing");
- return r;
+ goto failed_opts;
}
s->snap = NULL;
@@ -557,6 +557,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags)
NULL, qemu_rbd_aio_flush_cb, s);
+ qemu_opts_del(opts);
return 0;
failed:
@@ -566,6 +567,8 @@ failed_open:
failed_shutdown:
rados_shutdown(s->cluster);
g_free(s->snap);
+failed_opts:
+ qemu_opts_del(opts);
return r;
}
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 07/11] ide: refuse WIN_READ_NATIVE_MAX on empty device
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (11 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 06/11] rbd: Fix use after free in rbd_open() Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 07/11] sheepdog: cleanup find_vdi_name Stefan Hajnoczi
` (8 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Anthony Liguori, Stefan Hajnoczi
What is the highest addressable sector on an empty CD-ROM? Nothing is
addressable so produce an error.
This patch prevents a divide-by-zero in ide_set_sector() since
s->sectors and s->heads would be 0. Not to mention that a sector=-1
argument would be nonsense.
Note that WIN_READ_NATIVE_MAX can be triggered using hdparm -N 1024
/dev/cdrom. The LBA bit will be set to 1 though, so the only easy way
to go down the ide_set_sector() CHS code path which divides by zero is
to comment out the s->select & 0x40 case for testing.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
---
hw/ide/core.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 87d67b7..c7a8041 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -1262,6 +1262,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
lba48 = 1;
/* fall through */
case WIN_READ_NATIVE_MAX:
+ /* Refuse if no sectors are addressable (e.g. medium not inserted) */
+ if (s->nb_sectors == 0) {
+ goto abort_cmd;
+ }
ide_cmd_lba48_transform(s, lba48);
ide_set_sector(s, s->nb_sectors - 1);
s->status = READY_STAT | SEEK_STAT;
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 07/11] sheepdog: cleanup find_vdi_name
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (12 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 07/11] ide: refuse WIN_READ_NATIVE_MAX on empty device Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 08/11] block: Add support for Secure Shell (ssh) block device Stefan Hajnoczi
` (7 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Anthony Liguori, Stefan Hajnoczi, MORITA Kazutaka
From: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
This makes 'filename' and 'tag' constant variables, and renames
'for_snapshot' to 'lock' to clear how it works.
Signed-off-by: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/sheepdog.c | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 9f30a87..4326664 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -941,8 +941,9 @@ static int parse_vdiname(BDRVSheepdogState *s, const char *filename,
return ret;
}
-static int find_vdi_name(BDRVSheepdogState *s, char *filename, uint32_t snapid,
- char *tag, uint32_t *vid, int for_snapshot)
+static int find_vdi_name(BDRVSheepdogState *s, const char *filename,
+ uint32_t snapid, const char *tag, uint32_t *vid,
+ bool lock)
{
int ret, fd;
SheepdogVdiReq hdr;
@@ -963,10 +964,10 @@ static int find_vdi_name(BDRVSheepdogState *s, char *filename, uint32_t snapid,
strncpy(buf + SD_MAX_VDI_LEN, tag, SD_MAX_VDI_TAG_LEN);
memset(&hdr, 0, sizeof(hdr));
- if (for_snapshot) {
- hdr.opcode = SD_OP_GET_VDI_INFO;
- } else {
+ if (lock) {
hdr.opcode = SD_OP_LOCK_VDI;
+ } else {
+ hdr.opcode = SD_OP_GET_VDI_INFO;
}
wlen = SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN;
hdr.proto_ver = SD_PROTO_VER;
@@ -1205,7 +1206,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags)
goto out;
}
- ret = find_vdi_name(s, vdi, snapid, tag, &vid, 0);
+ ret = find_vdi_name(s, vdi, snapid, tag, &vid, true);
if (ret) {
goto out;
}
@@ -1921,7 +1922,7 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
pstrcpy(tag, sizeof(tag), s->name);
}
- ret = find_vdi_name(s, vdi, snapid, tag, &vid, 1);
+ ret = find_vdi_name(s, vdi, snapid, tag, &vid, false);
if (ret) {
error_report("Failed to find_vdi_name");
goto out;
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 08/11] block: Add support for Secure Shell (ssh) block device.
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (13 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 07/11] sheepdog: cleanup find_vdi_name Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 08/11] sheepdog: add SD_RES_READONLY result code Stefan Hajnoczi
` (6 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Stefan Hajnoczi, Anthony Liguori, Richard W.M. Jones,
Stefan Hajnoczi
From: "Richard W.M. Jones" <rjones@redhat.com>
qemu-system-x86_64 -drive file=ssh://hostname/some/image
QEMU will ssh into 'hostname' and open '/some/image' which is made
available as a standard block device.
You can specify a username (ssh://user@host/...) and/or a port number
(ssh://host:port/...). You can also use an alternate syntax using
properties (file.user, file.host, file.port, file.path).
Current limitations:
- Authentication must be done without passwords or passphrases, using
ssh-agent. Other authentication methods are not supported.
- Uses a single connection, instead of concurrent AIO with multiple
SSH connections.
This is implemented using libssh2 on the client side. The server just
requires a regular ssh daemon with sftp-server support. Most ssh
daemons on Unix/Linux systems will work out of the box.
Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
Cc: Stefan Hajnoczi <stefanha@gmail.com>
Cc: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/Makefile.objs | 1 +
block/ssh.c | 995 ++++++++++++++++++++++++++++++++++++++++++++++++++++
configure | 48 +++
qemu-doc.texi | 49 +++
qemu-options.hx | 12 +
5 files changed, 1105 insertions(+)
create mode 100644 block/ssh.c
diff --git a/block/Makefile.objs b/block/Makefile.objs
index c067f38..6c4b5bc 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -13,6 +13,7 @@ block-obj-$(CONFIG_LIBISCSI) += iscsi.o
block-obj-$(CONFIG_CURL) += curl.o
block-obj-$(CONFIG_RBD) += rbd.o
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
+block-obj-$(CONFIG_LIBSSH2) += ssh.o
endif
common-obj-y += stream.o
diff --git a/block/ssh.c b/block/ssh.c
new file mode 100644
index 0000000..89a9017
--- /dev/null
+++ b/block/ssh.c
@@ -0,0 +1,995 @@
+/*
+ * Secure Shell (ssh) backend for QEMU.
+ *
+ * Copyright (C) 2013 Red Hat Inc., Richard W.M. Jones <rjones@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <libssh2.h>
+#include <libssh2_sftp.h>
+
+#include "block/block_int.h"
+#include "qemu/sockets.h"
+#include "qemu/uri.h"
+#include "qapi/qmp/qint.h"
+
+/* DEBUG_SSH=1 enables the DPRINTF (debugging printf) statements in
+ * this block driver code.
+ *
+ * TRACE_LIBSSH2=<bitmask> enables tracing in libssh2 itself. Note
+ * that this requires that libssh2 was specially compiled with the
+ * `./configure --enable-debug' option, so most likely you will have
+ * to compile it yourself. The meaning of <bitmask> is described
+ * here: http://www.libssh2.org/libssh2_trace.html
+ */
+#define DEBUG_SSH 0
+#define TRACE_LIBSSH2 0 /* or try: LIBSSH2_TRACE_SFTP */
+
+#define DPRINTF(fmt, ...) \
+ do { \
+ if (DEBUG_SSH) { \
+ fprintf(stderr, "ssh: %-15s " fmt "\n", \
+ __func__, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+typedef struct BDRVSSHState {
+ /* Coroutine. */
+ CoMutex lock;
+
+ /* SSH connection. */
+ int sock; /* socket */
+ LIBSSH2_SESSION *session; /* ssh session */
+ LIBSSH2_SFTP *sftp; /* sftp session */
+ LIBSSH2_SFTP_HANDLE *sftp_handle; /* sftp remote file handle */
+
+ /* See ssh_seek() function below. */
+ int64_t offset;
+ bool offset_op_read;
+
+ /* File attributes at open. We try to keep the .filesize field
+ * updated if it changes (eg by writing at the end of the file).
+ */
+ LIBSSH2_SFTP_ATTRIBUTES attrs;
+} BDRVSSHState;
+
+static void ssh_state_init(BDRVSSHState *s)
+{
+ memset(s, 0, sizeof *s);
+ s->sock = -1;
+ s->offset = -1;
+ qemu_co_mutex_init(&s->lock);
+}
+
+static void ssh_state_free(BDRVSSHState *s)
+{
+ if (s->sftp_handle) {
+ libssh2_sftp_close(s->sftp_handle);
+ }
+ if (s->sftp) {
+ libssh2_sftp_shutdown(s->sftp);
+ }
+ if (s->session) {
+ libssh2_session_disconnect(s->session,
+ "from qemu ssh client: "
+ "user closed the connection");
+ libssh2_session_free(s->session);
+ }
+ if (s->sock >= 0) {
+ close(s->sock);
+ }
+}
+
+/* Wrappers around error_report which make sure to dump as much
+ * information from libssh2 as possible.
+ */
+static void
+session_error_report(BDRVSSHState *s, const char *fs, ...)
+{
+ va_list args;
+
+ va_start(args, fs);
+ error_vprintf(fs, args);
+
+ if ((s)->session) {
+ char *ssh_err;
+ int ssh_err_code;
+
+ libssh2_session_last_error((s)->session, &ssh_err, NULL, 0);
+ /* This is not an errno. See <libssh2.h>. */
+ ssh_err_code = libssh2_session_last_errno((s)->session);
+
+ error_printf(": %s (libssh2 error code: %d)", ssh_err, ssh_err_code);
+ }
+
+ va_end(args);
+ error_printf("\n");
+}
+
+static void
+sftp_error_report(BDRVSSHState *s, const char *fs, ...)
+{
+ va_list args;
+
+ va_start(args, fs);
+ error_vprintf(fs, args);
+
+ if ((s)->sftp) {
+ char *ssh_err;
+ int ssh_err_code;
+ unsigned long sftp_err_code;
+
+ libssh2_session_last_error((s)->session, &ssh_err, NULL, 0);
+ /* This is not an errno. See <libssh2.h>. */
+ ssh_err_code = libssh2_session_last_errno((s)->session);
+ /* See <libssh2_sftp.h>. */
+ sftp_err_code = libssh2_sftp_last_error((s)->sftp);
+
+ error_printf(": %s (libssh2 error code: %d, sftp error code: %lu)",
+ ssh_err, ssh_err_code, sftp_err_code);
+ }
+
+ va_end(args);
+ error_printf("\n");
+}
+
+static int parse_uri(const char *filename, QDict *options, Error **errp)
+{
+ URI *uri = NULL;
+ QueryParams *qp = NULL;
+ int i;
+
+ uri = uri_parse(filename);
+ if (!uri) {
+ return -EINVAL;
+ }
+
+ if (strcmp(uri->scheme, "ssh") != 0) {
+ error_setg(errp, "URI scheme must be 'ssh'");
+ goto err;
+ }
+
+ if (!uri->server || strcmp(uri->server, "") == 0) {
+ error_setg(errp, "missing hostname in URI");
+ goto err;
+ }
+
+ if (!uri->path || strcmp(uri->path, "") == 0) {
+ error_setg(errp, "missing remote path in URI");
+ goto err;
+ }
+
+ qp = query_params_parse(uri->query);
+ if (!qp) {
+ error_setg(errp, "could not parse query parameters");
+ goto err;
+ }
+
+ if(uri->user && strcmp(uri->user, "") != 0) {
+ qdict_put(options, "user", qstring_from_str(uri->user));
+ }
+
+ qdict_put(options, "host", qstring_from_str(uri->server));
+
+ if (uri->port) {
+ qdict_put(options, "port", qint_from_int(uri->port));
+ }
+
+ qdict_put(options, "path", qstring_from_str(uri->path));
+
+ /* Pick out any query parameters that we understand, and ignore
+ * the rest.
+ */
+ for (i = 0; i < qp->n; ++i) {
+ if (strcmp(qp->p[i].name, "host_key_check") == 0) {
+ qdict_put(options, "host_key_check",
+ qstring_from_str(qp->p[i].value));
+ }
+ }
+
+ query_params_free(qp);
+ uri_free(uri);
+ return 0;
+
+ err:
+ if (qp) {
+ query_params_free(qp);
+ }
+ if (uri) {
+ uri_free(uri);
+ }
+ return -EINVAL;
+}
+
+static void ssh_parse_filename(const char *filename, QDict *options,
+ Error **errp)
+{
+ if (qdict_haskey(options, "user") ||
+ qdict_haskey(options, "host") ||
+ qdict_haskey(options, "port") ||
+ qdict_haskey(options, "path") ||
+ qdict_haskey(options, "host_key_check")) {
+ error_setg(errp, "user, host, port, path, host_key_check cannot be used at the same time as a file option");
+ return;
+ }
+
+ parse_uri(filename, options, errp);
+}
+
+static int check_host_key_knownhosts(BDRVSSHState *s,
+ const char *host, int port)
+{
+ const char *home;
+ char *knh_file = NULL;
+ LIBSSH2_KNOWNHOSTS *knh = NULL;
+ struct libssh2_knownhost *found;
+ int ret, r;
+ const char *hostkey;
+ size_t len;
+ int type;
+
+ hostkey = libssh2_session_hostkey(s->session, &len, &type);
+ if (!hostkey) {
+ ret = -EINVAL;
+ session_error_report(s, "failed to read remote host key");
+ goto out;
+ }
+
+ knh = libssh2_knownhost_init(s->session);
+ if (!knh) {
+ ret = -EINVAL;
+ session_error_report(s, "failed to initialize known hosts support");
+ goto out;
+ }
+
+ home = getenv("HOME");
+ if (home) {
+ knh_file = g_strdup_printf("%s/.ssh/known_hosts", home);
+ } else {
+ knh_file = g_strdup_printf("/root/.ssh/known_hosts");
+ }
+
+ /* Read all known hosts from OpenSSH-style known_hosts file. */
+ libssh2_knownhost_readfile(knh, knh_file, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
+
+ r = libssh2_knownhost_checkp(knh, host, port, hostkey, len,
+ LIBSSH2_KNOWNHOST_TYPE_PLAIN|
+ LIBSSH2_KNOWNHOST_KEYENC_RAW,
+ &found);
+ switch (r) {
+ case LIBSSH2_KNOWNHOST_CHECK_MATCH:
+ /* OK */
+ DPRINTF("host key OK: %s", found->key);
+ break;
+ case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
+ ret = -EINVAL;
+ session_error_report(s, "host key does not match the one in known_hosts (found key %s)",
+ found->key);
+ goto out;
+ case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
+ ret = -EINVAL;
+ session_error_report(s, "no host key was found in known_hosts");
+ goto out;
+ case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
+ ret = -EINVAL;
+ session_error_report(s, "failure matching the host key with known_hosts");
+ goto out;
+ default:
+ ret = -EINVAL;
+ session_error_report(s, "unknown error matching the host key with known_hosts (%d)",
+ r);
+ goto out;
+ }
+
+ /* known_hosts checking successful. */
+ ret = 0;
+
+ out:
+ if (knh != NULL) {
+ libssh2_knownhost_free(knh);
+ }
+ g_free(knh_file);
+ return ret;
+}
+
+static unsigned hex2decimal(char ch)
+{
+ if (ch >= '0' && ch <= '9') {
+ return (ch - '0');
+ } else if (ch >= 'a' && ch <= 'f') {
+ return 10 + (ch - 'a');
+ } else if (ch >= 'A' && ch <= 'F') {
+ return 10 + (ch - 'A');
+ }
+
+ return -1;
+}
+
+/* Compare the binary fingerprint (hash of host key) with the
+ * host_key_check parameter.
+ */
+static int compare_fingerprint(const unsigned char *fingerprint, size_t len,
+ const char *host_key_check)
+{
+ unsigned c;
+
+ while (len > 0) {
+ while (*host_key_check == ':')
+ host_key_check++;
+ if (!qemu_isxdigit(host_key_check[0]) ||
+ !qemu_isxdigit(host_key_check[1]))
+ return 1;
+ c = hex2decimal(host_key_check[0]) * 16 +
+ hex2decimal(host_key_check[1]);
+ if (c - *fingerprint != 0)
+ return c - *fingerprint;
+ fingerprint++;
+ len--;
+ host_key_check += 2;
+ }
+ return *host_key_check - '\0';
+}
+
+static int
+check_host_key_hash(BDRVSSHState *s, const char *hash,
+ int hash_type, size_t fingerprint_len)
+{
+ const char *fingerprint;
+
+ fingerprint = libssh2_hostkey_hash(s->session, hash_type);
+ if (!fingerprint) {
+ session_error_report(s, "failed to read remote host key");
+ return -EINVAL;
+ }
+
+ if(compare_fingerprint((unsigned char *) fingerprint, fingerprint_len,
+ hash) != 0) {
+ error_report("remote host key does not match host_key_check '%s'",
+ hash);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static int check_host_key(BDRVSSHState *s, const char *host, int port,
+ const char *host_key_check)
+{
+ /* host_key_check=no */
+ if (strcmp(host_key_check, "no") == 0) {
+ return 0;
+ }
+
+ /* host_key_check=md5:xx:yy:zz:... */
+ if (strlen(host_key_check) >= 4 &&
+ strncmp(host_key_check, "md5:", 4) == 0) {
+ return check_host_key_hash(s, &host_key_check[4],
+ LIBSSH2_HOSTKEY_HASH_MD5, 16);
+ }
+
+ /* host_key_check=sha1:xx:yy:zz:... */
+ if (strlen(host_key_check) >= 5 &&
+ strncmp(host_key_check, "sha1:", 5) == 0) {
+ return check_host_key_hash(s, &host_key_check[5],
+ LIBSSH2_HOSTKEY_HASH_SHA1, 20);
+ }
+
+ /* host_key_check=yes */
+ if (strcmp(host_key_check, "yes") == 0) {
+ return check_host_key_knownhosts(s, host, port);
+ }
+
+ error_report("unknown host_key_check setting (%s)", host_key_check);
+ return -EINVAL;
+}
+
+static int authenticate(BDRVSSHState *s, const char *user)
+{
+ int r, ret;
+ const char *userauthlist;
+ LIBSSH2_AGENT *agent = NULL;
+ struct libssh2_agent_publickey *identity;
+ struct libssh2_agent_publickey *prev_identity = NULL;
+
+ userauthlist = libssh2_userauth_list(s->session, user, strlen(user));
+ if (strstr(userauthlist, "publickey") == NULL) {
+ ret = -EPERM;
+ error_report("remote server does not support \"publickey\" authentication");
+ goto out;
+ }
+
+ /* Connect to ssh-agent and try each identity in turn. */
+ agent = libssh2_agent_init(s->session);
+ if (!agent) {
+ ret = -EINVAL;
+ session_error_report(s, "failed to initialize ssh-agent support");
+ goto out;
+ }
+ if (libssh2_agent_connect(agent)) {
+ ret = -ECONNREFUSED;
+ session_error_report(s, "failed to connect to ssh-agent");
+ goto out;
+ }
+ if (libssh2_agent_list_identities(agent)) {
+ ret = -EINVAL;
+ session_error_report(s, "failed requesting identities from ssh-agent");
+ goto out;
+ }
+
+ for(;;) {
+ r = libssh2_agent_get_identity(agent, &identity, prev_identity);
+ if (r == 1) { /* end of list */
+ break;
+ }
+ if (r < 0) {
+ ret = -EINVAL;
+ session_error_report(s, "failed to obtain identity from ssh-agent");
+ goto out;
+ }
+ r = libssh2_agent_userauth(agent, user, identity);
+ if (r == 0) {
+ /* Authenticated! */
+ ret = 0;
+ goto out;
+ }
+ /* Failed to authenticate with this identity, try the next one. */
+ prev_identity = identity;
+ }
+
+ ret = -EPERM;
+ error_report("failed to authenticate using publickey authentication "
+ "and the identities held by your ssh-agent");
+
+ out:
+ if (agent != NULL) {
+ /* Note: libssh2 implementation implicitly calls
+ * libssh2_agent_disconnect if necessary.
+ */
+ libssh2_agent_free(agent);
+ }
+
+ return ret;
+}
+
+static int connect_to_ssh(BDRVSSHState *s, QDict *options,
+ int ssh_flags, int creat_mode)
+{
+ int r, ret;
+ Error *err = NULL;
+ const char *host, *user, *path, *host_key_check;
+ int port;
+ char *hostport = NULL;
+
+ host = qdict_get_str(options, "host");
+
+ if (qdict_haskey(options, "port")) {
+ port = qdict_get_int(options, "port");
+ } else {
+ port = 22;
+ }
+
+ path = qdict_get_str(options, "path");
+
+ if (qdict_haskey(options, "user")) {
+ user = qdict_get_str(options, "user");
+ } else {
+ user = g_get_user_name();
+ if (!user) {
+ ret = -errno;
+ goto err;
+ }
+ }
+
+ if (qdict_haskey(options, "host_key_check")) {
+ host_key_check = qdict_get_str(options, "host_key_check");
+ } else {
+ host_key_check = "yes";
+ }
+
+ /* Open the socket and connect. */
+ hostport = g_strdup_printf("%s:%d", host, port);
+ s->sock = inet_connect(hostport, &err);
+ if (err != NULL) {
+ ret = -errno;
+ qerror_report_err(err);
+ error_free(err);
+ goto err;
+ }
+
+ /* Create SSH session. */
+ s->session = libssh2_session_init();
+ if (!s->session) {
+ ret = -EINVAL;
+ session_error_report(s, "failed to initialize libssh2 session");
+ goto err;
+ }
+
+#if TRACE_LIBSSH2 != 0
+ libssh2_trace(s->session, TRACE_LIBSSH2);
+#endif
+
+ r = libssh2_session_handshake(s->session, s->sock);
+ if (r != 0) {
+ ret = -EINVAL;
+ session_error_report(s, "failed to establish SSH session");
+ goto err;
+ }
+
+ /* Check the remote host's key against known_hosts. */
+ ret = check_host_key(s, host, port, host_key_check);
+ if (ret < 0) {
+ goto err;
+ }
+
+ /* Authenticate. */
+ ret = authenticate(s, user);
+ if (ret < 0) {
+ goto err;
+ }
+
+ /* Start SFTP. */
+ s->sftp = libssh2_sftp_init(s->session);
+ if (!s->sftp) {
+ session_error_report(s, "failed to initialize sftp handle");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Open the remote file. */
+ DPRINTF("opening file %s flags=0x%x creat_mode=0%o",
+ path, ssh_flags, creat_mode);
+ s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode);
+ if (!s->sftp_handle) {
+ session_error_report(s, "failed to open remote file '%s'", path);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs);
+ if (r < 0) {
+ sftp_error_report(s, "failed to read file attributes");
+ return -EINVAL;
+ }
+
+ /* Delete the options we've used; any not deleted will cause the
+ * block layer to give an error about unused options.
+ */
+ qdict_del(options, "host");
+ qdict_del(options, "port");
+ qdict_del(options, "user");
+ qdict_del(options, "path");
+ qdict_del(options, "host_key_check");
+
+ g_free(hostport);
+ return 0;
+
+ err:
+ if (s->sftp_handle) {
+ libssh2_sftp_close(s->sftp_handle);
+ }
+ s->sftp_handle = NULL;
+ if (s->sftp) {
+ libssh2_sftp_shutdown(s->sftp);
+ }
+ s->sftp = NULL;
+ if (s->session) {
+ libssh2_session_disconnect(s->session,
+ "from qemu ssh client: "
+ "error opening connection");
+ libssh2_session_free(s->session);
+ }
+ s->session = NULL;
+ g_free(hostport);
+
+ return ret;
+}
+
+static int ssh_file_open(BlockDriverState *bs, const char *filename,
+ QDict *options, int bdrv_flags)
+{
+ BDRVSSHState *s = bs->opaque;
+ int ret;
+ int ssh_flags;
+
+ ssh_state_init(s);
+
+ ssh_flags = LIBSSH2_FXF_READ;
+ if (bdrv_flags & BDRV_O_RDWR) {
+ ssh_flags |= LIBSSH2_FXF_WRITE;
+ }
+
+ /* Start up SSH. */
+ ret = connect_to_ssh(s, options, ssh_flags, 0);
+ if (ret < 0) {
+ goto err;
+ }
+
+ /* Go non-blocking. */
+ libssh2_session_set_blocking(s->session, 0);
+
+ return 0;
+
+ err:
+ if (s->sock >= 0) {
+ close(s->sock);
+ }
+ s->sock = -1;
+
+ return ret;
+}
+
+static QEMUOptionParameter ssh_create_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ { NULL }
+};
+
+static int ssh_create(const char *filename, QEMUOptionParameter *options)
+{
+ int r, ret;
+ Error *local_err = NULL;
+ int64_t total_size = 0;
+ QDict *uri_options = NULL;
+ BDRVSSHState s;
+ ssize_t r2;
+ char c[1] = { '\0' };
+
+ ssh_state_init(&s);
+
+ /* Get desired file size. */
+ while (options && options->name) {
+ if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+ total_size = options->value.n;
+ }
+ options++;
+ }
+ DPRINTF("total_size=%" PRIi64, total_size);
+
+ uri_options = qdict_new();
+ r = parse_uri(filename, uri_options, &local_err);
+ if (r < 0) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ ret = r;
+ goto out;
+ }
+
+ r = connect_to_ssh(&s, uri_options,
+ LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
+ LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, 0644);
+ if (r < 0) {
+ ret = r;
+ goto out;
+ }
+
+ if (total_size > 0) {
+ libssh2_sftp_seek64(s.sftp_handle, total_size-1);
+ r2 = libssh2_sftp_write(s.sftp_handle, c, 1);
+ if (r2 < 0) {
+ sftp_error_report(&s, "truncate failed");
+ ret = -EINVAL;
+ goto out;
+ }
+ s.attrs.filesize = total_size;
+ }
+
+ ret = 0;
+
+ out:
+ ssh_state_free(&s);
+ if (uri_options != NULL) {
+ QDECREF(uri_options);
+ }
+ return ret;
+}
+
+static void ssh_close(BlockDriverState *bs)
+{
+ BDRVSSHState *s = bs->opaque;
+
+ ssh_state_free(s);
+}
+
+static void restart_coroutine(void *opaque)
+{
+ Coroutine *co = opaque;
+
+ DPRINTF("co=%p", co);
+
+ qemu_coroutine_enter(co, NULL);
+}
+
+/* Always true because when we have called set_fd_handler there is
+ * always a request being processed.
+ */
+static int return_true(void *opaque)
+{
+ return 1;
+}
+
+static coroutine_fn void set_fd_handler(BDRVSSHState *s)
+{
+ int r;
+ IOHandler *rd_handler = NULL, *wr_handler = NULL;
+ Coroutine *co = qemu_coroutine_self();
+
+ r = libssh2_session_block_directions(s->session);
+
+ if (r & LIBSSH2_SESSION_BLOCK_INBOUND) {
+ rd_handler = restart_coroutine;
+ }
+ if (r & LIBSSH2_SESSION_BLOCK_OUTBOUND) {
+ wr_handler = restart_coroutine;
+ }
+
+ DPRINTF("s->sock=%d rd_handler=%p wr_handler=%p", s->sock,
+ rd_handler, wr_handler);
+
+ qemu_aio_set_fd_handler(s->sock, rd_handler, wr_handler, return_true, co);
+}
+
+static coroutine_fn void clear_fd_handler(BDRVSSHState *s)
+{
+ DPRINTF("s->sock=%d", s->sock);
+ qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL, NULL);
+}
+
+/* A non-blocking call returned EAGAIN, so yield, ensuring the
+ * handlers are set up so that we'll be rescheduled when there is an
+ * interesting event on the socket.
+ */
+static coroutine_fn void co_yield(BDRVSSHState *s)
+{
+ set_fd_handler(s);
+ qemu_coroutine_yield();
+ clear_fd_handler(s);
+}
+
+/* SFTP has a function `libssh2_sftp_seek64' which seeks to a position
+ * in the remote file. Notice that it just updates a field in the
+ * sftp_handle structure, so there is no network traffic and it cannot
+ * fail.
+ *
+ * However, `libssh2_sftp_seek64' does have a catastrophic effect on
+ * performance since it causes the handle to throw away all in-flight
+ * reads and buffered readahead data. Therefore this function tries
+ * to be intelligent about when to call the underlying libssh2 function.
+ */
+#define SSH_SEEK_WRITE 0
+#define SSH_SEEK_READ 1
+#define SSH_SEEK_FORCE 2
+
+static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags)
+{
+ bool op_read = (flags & SSH_SEEK_READ) != 0;
+ bool force = (flags & SSH_SEEK_FORCE) != 0;
+
+ if (force || op_read != s->offset_op_read || offset != s->offset) {
+ DPRINTF("seeking to offset=%" PRIi64, offset);
+ libssh2_sftp_seek64(s->sftp_handle, offset);
+ s->offset = offset;
+ s->offset_op_read = op_read;
+ }
+}
+
+static coroutine_fn int ssh_read(BDRVSSHState *s,
+ int64_t offset, size_t size,
+ QEMUIOVector *qiov)
+{
+ ssize_t r;
+ size_t got;
+ char *buf, *end_of_vec;
+ struct iovec *i;
+
+ DPRINTF("offset=%" PRIi64 " size=%zu", offset, size);
+
+ ssh_seek(s, offset, SSH_SEEK_READ);
+
+ /* This keeps track of the current iovec element ('i'), where we
+ * will write to next ('buf'), and the end of the current iovec
+ * ('end_of_vec').
+ */
+ i = &qiov->iov[0];
+ buf = i->iov_base;
+ end_of_vec = i->iov_base + i->iov_len;
+
+ /* libssh2 has a hard-coded limit of 2000 bytes per request,
+ * although it will also do readahead behind our backs. Therefore
+ * we may have to do repeated reads here until we have read 'size'
+ * bytes.
+ */
+ for (got = 0; got < size; ) {
+ again:
+ DPRINTF("sftp_read buf=%p size=%zu", buf, end_of_vec - buf);
+ r = libssh2_sftp_read(s->sftp_handle, buf, end_of_vec - buf);
+ DPRINTF("sftp_read returned %zd", r);
+
+ if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
+ co_yield(s);
+ goto again;
+ }
+ if (r < 0) {
+ sftp_error_report(s, "read failed");
+ s->offset = -1;
+ return -EIO;
+ }
+ if (r == 0) {
+ /* EOF: Short read so pad the buffer with zeroes and return it. */
+ qemu_iovec_memset(qiov, got, 0, size - got);
+ return 0;
+ }
+
+ got += r;
+ buf += r;
+ s->offset += r;
+ if (buf >= end_of_vec && got < size) {
+ i++;
+ buf = i->iov_base;
+ end_of_vec = i->iov_base + i->iov_len;
+ }
+ }
+
+ return 0;
+}
+
+static coroutine_fn int ssh_co_readv(BlockDriverState *bs,
+ int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov)
+{
+ BDRVSSHState *s = bs->opaque;
+ int ret;
+
+ qemu_co_mutex_lock(&s->lock);
+ ret = ssh_read(s, sector_num * BDRV_SECTOR_SIZE,
+ nb_sectors * BDRV_SECTOR_SIZE, qiov);
+ qemu_co_mutex_unlock(&s->lock);
+
+ return ret;
+}
+
+static int ssh_write(BDRVSSHState *s,
+ int64_t offset, size_t size,
+ QEMUIOVector *qiov)
+{
+ ssize_t r;
+ size_t written;
+ char *buf, *end_of_vec;
+ struct iovec *i;
+
+ DPRINTF("offset=%" PRIi64 " size=%zu", offset, size);
+
+ ssh_seek(s, offset, SSH_SEEK_WRITE);
+
+ /* This keeps track of the current iovec element ('i'), where we
+ * will read from next ('buf'), and the end of the current iovec
+ * ('end_of_vec').
+ */
+ i = &qiov->iov[0];
+ buf = i->iov_base;
+ end_of_vec = i->iov_base + i->iov_len;
+
+ for (written = 0; written < size; ) {
+ again:
+ DPRINTF("sftp_write buf=%p size=%zu", buf, end_of_vec - buf);
+ r = libssh2_sftp_write(s->sftp_handle, buf, end_of_vec - buf);
+ DPRINTF("sftp_write returned %zd", r);
+
+ if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
+ co_yield(s);
+ goto again;
+ }
+ if (r < 0) {
+ sftp_error_report(s, "write failed");
+ s->offset = -1;
+ return -EIO;
+ }
+ /* The libssh2 API is very unclear about this. A comment in
+ * the code says "nothing was acked, and no EAGAIN was
+ * received!" which apparently means that no data got sent
+ * out, and the underlying channel didn't return any EAGAIN
+ * indication. I think this is a bug in either libssh2 or
+ * OpenSSH (server-side). In any case, forcing a seek (to
+ * discard libssh2 internal buffers), and then trying again
+ * works for me.
+ */
+ if (r == 0) {
+ ssh_seek(s, offset + written, SSH_SEEK_WRITE|SSH_SEEK_FORCE);
+ co_yield(s);
+ goto again;
+ }
+
+ written += r;
+ buf += r;
+ s->offset += r;
+ if (buf >= end_of_vec && written < size) {
+ i++;
+ buf = i->iov_base;
+ end_of_vec = i->iov_base + i->iov_len;
+ }
+
+ if (offset + written > s->attrs.filesize)
+ s->attrs.filesize = offset + written;
+ }
+
+ return 0;
+}
+
+static coroutine_fn int ssh_co_writev(BlockDriverState *bs,
+ int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov)
+{
+ BDRVSSHState *s = bs->opaque;
+ int ret;
+
+ qemu_co_mutex_lock(&s->lock);
+ ret = ssh_write(s, sector_num * BDRV_SECTOR_SIZE,
+ nb_sectors * BDRV_SECTOR_SIZE, qiov);
+ qemu_co_mutex_unlock(&s->lock);
+
+ return ret;
+}
+
+static int64_t ssh_getlength(BlockDriverState *bs)
+{
+ BDRVSSHState *s = bs->opaque;
+ int64_t length;
+
+ /* Note we cannot make a libssh2 call here. */
+ length = (int64_t) s->attrs.filesize;
+ DPRINTF("length=%" PRIi64, length);
+
+ return length;
+}
+
+static BlockDriver bdrv_ssh = {
+ .format_name = "ssh",
+ .protocol_name = "ssh",
+ .instance_size = sizeof(BDRVSSHState),
+ .bdrv_parse_filename = ssh_parse_filename,
+ .bdrv_file_open = ssh_file_open,
+ .bdrv_create = ssh_create,
+ .bdrv_close = ssh_close,
+ .bdrv_co_readv = ssh_co_readv,
+ .bdrv_co_writev = ssh_co_writev,
+ .bdrv_getlength = ssh_getlength,
+ .create_options = ssh_create_options,
+};
+
+static void bdrv_ssh_init(void)
+{
+ int r;
+
+ r = libssh2_init(0);
+ if (r != 0) {
+ fprintf(stderr, "libssh2 initialization failed, %d\n", r);
+ exit(EXIT_FAILURE);
+ }
+
+ bdrv_register(&bdrv_ssh);
+}
+
+block_init(bdrv_ssh_init);
diff --git a/configure b/configure
index 73df181..e6c5d2c 100755
--- a/configure
+++ b/configure
@@ -239,6 +239,7 @@ virtio_blk_data_plane=""
gtk=""
gtkabi="2.0"
tpm="no"
+libssh2=""
# parse CC options first
for opt do
@@ -922,6 +923,10 @@ for opt do
;;
--enable-tpm) tpm="yes"
;;
+ --disable-libssh2) libssh2="no"
+ ;;
+ --enable-libssh2) libssh2="yes"
+ ;;
*) echo "ERROR: unknown option $opt"; show_help="yes"
;;
esac
@@ -1182,6 +1187,8 @@ echo " --disable-glusterfs disable GlusterFS backend"
echo " --enable-gcov enable test coverage analysis with gcov"
echo " --gcov=GCOV use specified gcov [$gcov_tool]"
echo " --enable-tpm enable TPM support"
+echo " --disable-libssh2 disable ssh block device support"
+echo " --enable-libssh2 enable ssh block device support"
echo ""
echo "NOTE: The object files are built at the place where configure is launched"
exit 1
@@ -2314,6 +2321,42 @@ EOF
fi
##########################################
+# libssh2 probe
+if test "$libssh2" != "no" ; then
+ cat > $TMPC <<EOF
+#include <stdio.h>
+#include <libssh2.h>
+#include <libssh2_sftp.h>
+int main(void) {
+ LIBSSH2_SESSION *session;
+ session = libssh2_session_init ();
+ (void) libssh2_sftp_init (session);
+ return 0;
+}
+EOF
+
+ if $pkg_config libssh2 --modversion >/dev/null 2>&1; then
+ libssh2_cflags=`$pkg_config libssh2 --cflags`
+ libssh2_libs=`$pkg_config libssh2 --libs`
+ else
+ libssh2_cflags=
+ libssh2_libs="-lssh2"
+ fi
+
+ if compile_prog "$libssh2_cflags" "$libssh2_libs" ; then
+ libssh2=yes
+ libs_tools="$libssh2_libs $libs_tools"
+ libs_softmmu="$libssh2_libs $libs_softmmu"
+ QEMU_CFLAGS="$QEMU_CFLAGS $libssh2_cflags"
+ else
+ if test "$libssh2" = "yes" ; then
+ feature_not_found "libssh2"
+ fi
+ libssh2=no
+ fi
+fi
+
+##########################################
# linux-aio probe
if test "$linux_aio" != "no" ; then
@@ -3435,6 +3478,7 @@ echo "virtio-blk-data-plane $virtio_blk_data_plane"
echo "gcov $gcov_tool"
echo "gcov enabled $gcov"
echo "TPM support $tpm"
+echo "libssh2 support $libssh2"
if test "$sdl_too_old" = "yes"; then
echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -3793,6 +3837,10 @@ if test "$glusterfs" = "yes" ; then
echo "CONFIG_GLUSTERFS=y" >> $config_host_mak
fi
+if test "$libssh2" = "yes" ; then
+ echo "CONFIG_LIBSSH2=y" >> $config_host_mak
+fi
+
if test "$virtio_blk_data_plane" = "yes" ; then
echo 'CONFIG_VIRTIO_BLK_DATA_PLANE=$(CONFIG_VIRTIO)' >> $config_host_mak
fi
diff --git a/qemu-doc.texi b/qemu-doc.texi
index af84bef..5b36004 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -423,6 +423,7 @@ snapshots.
* disk_images_sheepdog:: Sheepdog disk images
* disk_images_iscsi:: iSCSI LUNs
* disk_images_gluster:: GlusterFS disk images
+* disk_images_ssh:: Secure Shell (ssh) disk images
@end menu
@node disk_images_quickstart
@@ -1038,6 +1039,54 @@ qemu-system-x86_64 -drive file=gluster+unix:///testvol/dir/a.img?socket=/tmp/glu
qemu-system-x86_64 -drive file=gluster+rdma://1.2.3.4:24007/testvol/a.img
@end example
+@node disk_images_ssh
+@subsection Secure Shell (ssh) disk images
+
+You can access disk images located on a remote ssh server
+by using the ssh protocol:
+
+@example
+qemu-system-x86_64 -drive file=ssh://[@var{user}@@]@var{server}[:@var{port}]/@var{path}[?host_key_check=@var{host_key_check}]
+@end example
+
+Alternative syntax using properties:
+
+@example
+qemu-system-x86_64 -drive file.driver=ssh[,file.user=@var{user}],file.host=@var{server}[,file.port=@var{port}],file.path=@var{path}[,file.host_key_check=@var{host_key_check}]
+@end example
+
+@var{ssh} is the protocol.
+
+@var{user} is the remote user. If not specified, then the local
+username is tried.
+
+@var{server} specifies the remote ssh server. Any ssh server can be
+used, but it must implement the sftp-server protocol. Most Unix/Linux
+systems should work without requiring any extra configuration.
+
+@var{port} is the port number on which sshd is listening. By default
+the standard ssh port (22) is used.
+
+@var{path} is the path to the disk image.
+
+The optional @var{host_key_check} parameter controls how the remote
+host's key is checked. The default is @code{yes} which means to use
+the local @file{.ssh/known_hosts} file. Setting this to @code{no}
+turns off known-hosts checking. Or you can check that the host key
+matches a specific fingerprint:
+@code{host_key_check=md5:78:45:8e:14:57:4f:d5:45:83:0a:0e:f3:49:82:c9:c8}
+(@code{sha1:} can also be used as a prefix, but note that OpenSSH
+tools only use MD5 to print fingerprints).
+
+Currently authentication must be done using ssh-agent. Other
+authentication methods may be supported in future.
+
+Note: The ssh driver does not obey disk flush requests (ie. to commit
+data to the backing disk when the guest requests it). This is because
+the underlying protocol (SFTP) does not support this. Thus there is a
+risk of guest disk corruption if the remote server or network goes
+down during writes.
+
@node pcsys_network
@section Network emulation
diff --git a/qemu-options.hx b/qemu-options.hx
index 7cd6002..5c115d1 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2107,6 +2107,18 @@ Example for Unix Domain Sockets
qemu-system-i386 --drive file=nbd:unix:/tmp/nbd-socket
@end example
+@item SSH
+QEMU supports SSH (Secure Shell) access to remote disks.
+
+Examples:
+@example
+qemu-system-i386 -drive file=ssh://user@@host/path/to/disk.img
+qemu-system-i386 -drive file.driver=ssh,file.user=user,file.host=host,file.port=22,file.path=/path/to/disk.img
+@end example
+
+Currently authentication must be done using ssh-agent. Other
+authentication methods may be supported in future.
+
@item Sheepdog
Sheepdog is a distributed storage system for QEMU.
QEMU supports using either local sheepdog devices or remote networked
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 08/11] sheepdog: add SD_RES_READONLY result code
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (14 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 08/11] block: Add support for Secure Shell (ssh) block device Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 09/11] block: ssh: Use libssh2_sftp_fsync (if supported by libssh2) to flush to disk Stefan Hajnoczi
` (5 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Anthony Liguori, Stefan Hajnoczi, MORITA Kazutaka
From: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
Sheepdog returns SD_RES_READONLY when qemu sends write requests to the
snapshot vdi. This adds the result code and makes sd_strerror() print
its error reason.
Signed-off-by: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/sheepdog.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 4326664..f4e7204 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -68,6 +68,7 @@
#define SD_RES_WAIT_FOR_JOIN 0x17 /* Waiting for other nodes joining */
#define SD_RES_JOIN_FAILED 0x18 /* Target node had failed to join sheepdog */
#define SD_RES_HALT 0x19 /* Sheepdog is stopped serving IO request */
+#define SD_RES_READONLY 0x1A /* Object is read-only */
/*
* Object ID rules
@@ -349,6 +350,7 @@ static const char * sd_strerror(int err)
{SD_RES_WAIT_FOR_JOIN, "Sheepdog is waiting for other nodes joining"},
{SD_RES_JOIN_FAILED, "Target node had failed to join sheepdog"},
{SD_RES_HALT, "Sheepdog is stopped serving IO request"},
+ {SD_RES_READONLY, "Object is read-only"},
};
for (i = 0; i < ARRAY_SIZE(errors); ++i) {
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 09/11] block: ssh: Use libssh2_sftp_fsync (if supported by libssh2) to flush to disk.
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (15 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 08/11] sheepdog: add SD_RES_READONLY result code Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 09/11] sheepdog: add helper function to reload inode Stefan Hajnoczi
` (4 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Anthony Liguori, Richard W.M. Jones, Stefan Hajnoczi
From: "Richard W.M. Jones" <rjones@redhat.com>
libssh2_sftp_fsync is an extension to libssh2 to support fsync(2) over
sftp, which is itself an extension of OpenSSH.
If both libssh2 and the ssh daemon support it, this will allow
bdrv_flush_to_disk to commit changes through to disk on the remote
server.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/ssh.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
configure | 25 +++++++++++++++++++
qemu-doc.texi | 15 ++++++++----
3 files changed, 108 insertions(+), 10 deletions(-)
diff --git a/block/ssh.c b/block/ssh.c
index 89a9017..8f78e2e 100644
--- a/block/ssh.c
+++ b/block/ssh.c
@@ -72,6 +72,10 @@ typedef struct BDRVSSHState {
* updated if it changes (eg by writing at the end of the file).
*/
LIBSSH2_SFTP_ATTRIBUTES attrs;
+
+ /* Used to warn if 'flush' is not supported. */
+ char *hostport;
+ bool unsafe_flush_warning;
} BDRVSSHState;
static void ssh_state_init(BDRVSSHState *s)
@@ -84,6 +88,7 @@ static void ssh_state_init(BDRVSSHState *s)
static void ssh_state_free(BDRVSSHState *s)
{
+ g_free(s->hostport);
if (s->sftp_handle) {
libssh2_sftp_close(s->sftp_handle);
}
@@ -479,7 +484,6 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
Error *err = NULL;
const char *host, *user, *path, *host_key_check;
int port;
- char *hostport = NULL;
host = qdict_get_str(options, "host");
@@ -507,9 +511,12 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
host_key_check = "yes";
}
+ /* Construct the host:port name for inet_connect. */
+ g_free(s->hostport);
+ s->hostport = g_strdup_printf("%s:%d", host, port);
+
/* Open the socket and connect. */
- hostport = g_strdup_printf("%s:%d", host, port);
- s->sock = inet_connect(hostport, &err);
+ s->sock = inet_connect(s->hostport, &err);
if (err != NULL) {
ret = -errno;
qerror_report_err(err);
@@ -581,7 +588,6 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
qdict_del(options, "path");
qdict_del(options, "host_key_check");
- g_free(hostport);
return 0;
err:
@@ -600,7 +606,6 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
libssh2_session_free(s->session);
}
s->session = NULL;
- g_free(hostport);
return ret;
}
@@ -953,6 +958,68 @@ static coroutine_fn int ssh_co_writev(BlockDriverState *bs,
return ret;
}
+static void unsafe_flush_warning(BDRVSSHState *s, const char *what)
+{
+ if (!s->unsafe_flush_warning) {
+ error_report("warning: ssh server %s does not support fsync",
+ s->hostport);
+ if (what) {
+ error_report("to support fsync, you need %s", what);
+ }
+ s->unsafe_flush_warning = true;
+ }
+}
+
+#ifdef HAS_LIBSSH2_SFTP_FSYNC
+
+static coroutine_fn int ssh_flush(BDRVSSHState *s)
+{
+ int r;
+
+ DPRINTF("fsync");
+ again:
+ r = libssh2_sftp_fsync(s->sftp_handle);
+ if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
+ co_yield(s);
+ goto again;
+ }
+ if (r == LIBSSH2_ERROR_SFTP_PROTOCOL &&
+ libssh2_sftp_last_error(s->sftp) == LIBSSH2_FX_OP_UNSUPPORTED) {
+ unsafe_flush_warning(s, "OpenSSH >= 6.3");
+ return 0;
+ }
+ if (r < 0) {
+ sftp_error_report(s, "fsync failed");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
+{
+ BDRVSSHState *s = bs->opaque;
+ int ret;
+
+ qemu_co_mutex_lock(&s->lock);
+ ret = ssh_flush(s);
+ qemu_co_mutex_unlock(&s->lock);
+
+ return ret;
+}
+
+#else /* !HAS_LIBSSH2_SFTP_FSYNC */
+
+static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
+{
+ BDRVSSHState *s = bs->opaque;
+
+ unsafe_flush_warning(s, "libssh2 >= 1.4.4");
+ return 0;
+}
+
+#endif /* !HAS_LIBSSH2_SFTP_FSYNC */
+
static int64_t ssh_getlength(BlockDriverState *bs)
{
BDRVSSHState *s = bs->opaque;
@@ -976,6 +1043,7 @@ static BlockDriver bdrv_ssh = {
.bdrv_co_readv = ssh_co_readv,
.bdrv_co_writev = ssh_co_writev,
.bdrv_getlength = ssh_getlength,
+ .bdrv_co_flush_to_disk = ssh_co_flush,
.create_options = ssh_create_options,
};
diff --git a/configure b/configure
index e6c5d2c..a97bf31 100755
--- a/configure
+++ b/configure
@@ -2357,6 +2357,31 @@ EOF
fi
##########################################
+# libssh2_sftp_fsync probe
+
+if test "$libssh2" = "yes"; then
+ cat > $TMPC <<EOF
+#include <stdio.h>
+#include <libssh2.h>
+#include <libssh2_sftp.h>
+int main(void) {
+ LIBSSH2_SESSION *session;
+ LIBSSH2_SFTP *sftp;
+ LIBSSH2_SFTP_HANDLE *sftp_handle;
+ session = libssh2_session_init ();
+ sftp = libssh2_sftp_init (session);
+ sftp_handle = libssh2_sftp_open (sftp, "/", 0, 0);
+ libssh2_sftp_fsync (sftp_handle);
+ return 0;
+}
+EOF
+ # libssh2_cflags/libssh2_libs defined in previous test.
+ if compile_prog "$libssh2_cflags" "$libssh2_libs" ; then
+ QEMU_CFLAGS="-DHAS_LIBSSH2_SFTP_FSYNC $QEMU_CFLAGS"
+ fi
+fi
+
+##########################################
# linux-aio probe
if test "$linux_aio" != "no" ; then
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 5b36004..dfea4d3 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -1081,11 +1081,16 @@ tools only use MD5 to print fingerprints).
Currently authentication must be done using ssh-agent. Other
authentication methods may be supported in future.
-Note: The ssh driver does not obey disk flush requests (ie. to commit
-data to the backing disk when the guest requests it). This is because
-the underlying protocol (SFTP) does not support this. Thus there is a
-risk of guest disk corruption if the remote server or network goes
-down during writes.
+Note: Many ssh servers do not support an @code{fsync}-style operation.
+The ssh driver cannot guarantee that disk flush requests are
+obeyed, and this causes a risk of disk corruption if the remote
+server or network goes down during writes. The driver will
+print a warning when @code{fsync} is not supported:
+
+warning: ssh server @code{ssh.example.com:22} does not support fsync
+
+With sufficiently new versions of libssh2 and OpenSSH, @code{fsync} is
+supported.
@node pcsys_network
@section Network emulation
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 09/11] sheepdog: add helper function to reload inode
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (16 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 09/11] block: ssh: Use libssh2_sftp_fsync (if supported by libssh2) to flush to disk Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 10/11] iotests: Add 'check -ssh' option to test Secure Shell block device Stefan Hajnoczi
` (3 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Anthony Liguori, Stefan Hajnoczi, MORITA Kazutaka
From: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
This adds a helper function to update the current inode state with the
specified vdi object.
Signed-off-by: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/sheepdog.c | 67 +++++++++++++++++++++++++++++++++-----------------------
1 file changed, 39 insertions(+), 28 deletions(-)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index f4e7204..0eaf4c3 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -1150,6 +1150,42 @@ static int write_object(int fd, char *buf, uint64_t oid, int copies,
create, cache_flags);
}
+/* update inode with the latest state */
+static int reload_inode(BDRVSheepdogState *s, uint32_t snapid, const char *tag)
+{
+ SheepdogInode *inode;
+ int ret = 0, fd;
+ uint32_t vid = 0;
+
+ fd = connect_to_sdog(s);
+ if (fd < 0) {
+ return -EIO;
+ }
+
+ inode = g_malloc(sizeof(s->inode));
+
+ ret = find_vdi_name(s, s->name, snapid, tag, &vid, false);
+ if (ret) {
+ goto out;
+ }
+
+ ret = read_object(fd, (char *)inode, vid_to_vdi_oid(vid),
+ s->inode.nr_copies, sizeof(*inode), 0, s->cache_flags);
+ if (ret < 0) {
+ goto out;
+ }
+
+ if (inode->vdi_id != s->inode.vdi_id) {
+ memcpy(&s->inode, inode, sizeof(s->inode));
+ }
+
+out:
+ g_free(inode);
+ closesocket(fd);
+
+ return ret;
+}
+
/* TODO Convert to fine grained options */
static QemuOptsList runtime_opts = {
.name = "sheepdog",
@@ -1905,18 +1941,14 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
{
BDRVSheepdogState *s = bs->opaque;
BDRVSheepdogState *old_s;
- char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
- char *buf = NULL;
- uint32_t vid;
+ char tag[SD_MAX_VDI_TAG_LEN];
uint32_t snapid = 0;
- int ret = 0, fd;
+ int ret = 0;
old_s = g_malloc(sizeof(BDRVSheepdogState));
memcpy(old_s, s, sizeof(BDRVSheepdogState));
- pstrcpy(vdi, sizeof(vdi), s->name);
-
snapid = strtoul(snapshot_id, NULL, 10);
if (snapid) {
tag[0] = 0;
@@ -1924,30 +1956,11 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
pstrcpy(tag, sizeof(tag), s->name);
}
- ret = find_vdi_name(s, vdi, snapid, tag, &vid, false);
+ ret = reload_inode(s, snapid, tag);
if (ret) {
- error_report("Failed to find_vdi_name");
goto out;
}
- fd = connect_to_sdog(s);
- if (fd < 0) {
- ret = fd;
- goto out;
- }
-
- buf = g_malloc(SD_INODE_SIZE);
- ret = read_object(fd, buf, vid_to_vdi_oid(vid), s->inode.nr_copies,
- SD_INODE_SIZE, 0, s->cache_flags);
-
- closesocket(fd);
-
- if (ret) {
- goto out;
- }
-
- memcpy(&s->inode, buf, sizeof(s->inode));
-
if (!s->inode.vm_state_size) {
error_report("Invalid snapshot");
ret = -ENOENT;
@@ -1956,14 +1969,12 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
s->is_snapshot = true;
- g_free(buf);
g_free(old_s);
return 0;
out:
/* recover bdrv_sd_state */
memcpy(s, old_s, sizeof(BDRVSheepdogState));
- g_free(buf);
g_free(old_s);
error_report("failed to open. recover old bdrv_sd_state.");
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 10/11] iotests: Add 'check -ssh' option to test Secure Shell block device.
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (17 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 09/11] sheepdog: add helper function to reload inode Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 10/11] sheepdog: resend write requests when SD_RES_READONLY is received Stefan Hajnoczi
` (2 subsequent siblings)
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Anthony Liguori, Richard W.M. Jones, Stefan Hajnoczi
From: "Richard W.M. Jones" <rjones@redhat.com>
Note in order to run these tests on ssh, you must be running a local
ssh daemon, and that daemon must accept loopback connections, and
ssh-agent has to be set up to allow logins on the local daemon. In
other words, the following command should just work without demanding
any passphrase:
ssh localhost
Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
tests/qemu-iotests/common | 5 +++++
tests/qemu-iotests/common.rc | 3 +++
2 files changed, 8 insertions(+)
diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common
index b3aad89..6826ea7 100644
--- a/tests/qemu-iotests/common
+++ b/tests/qemu-iotests/common
@@ -137,6 +137,7 @@ check options
-rbd test rbd
-sheepdog test sheepdog
-nbd test nbd
+ -ssh test ssh
-xdiff graphical mode diff
-nocache use O_DIRECT on backing file
-misalign misalign memory allocations
@@ -206,6 +207,10 @@ testlist options
IMGPROTO=nbd
xpand=false
;;
+ -ssh)
+ IMGPROTO=ssh
+ xpand=false
+ ;;
-nocache)
QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --nocache"
xpand=false
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index e522d61..a536bf7 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -52,6 +52,9 @@ if [ "$IMGPROTO" = "file" ]; then
elif [ "$IMGPROTO" = "nbd" ]; then
TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
TEST_IMG="nbd:127.0.0.1:10810"
+elif [ "$IMGPROTO" = "ssh" ]; then
+ TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
+ TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE"
else
TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT
fi
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 10/11] sheepdog: resend write requests when SD_RES_READONLY is received
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (18 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 10/11] iotests: Add 'check -ssh' option to test Secure Shell block device Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 11/11] rbd: add an asynchronous flush Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 11/11] sheepdog: fix loadvm operation Stefan Hajnoczi
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Anthony Liguori, Stefan Hajnoczi, MORITA Kazutaka
From: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
When a snapshot is taken from out side of qemu (e.g. qemu-img
snapshot), write requests to the current vdi return SD_RES_READONLY.
In this case, the sheepdog block driver needs to update the current
inode to the latest one and resend the write requests.
Signed-off-by: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/sheepdog.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 59 insertions(+), 1 deletion(-)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 0eaf4c3..77e21fd 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -605,6 +605,7 @@ static int do_req(int sockfd, SheepdogReq *hdr, void *data,
static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
struct iovec *iov, int niov, bool create,
enum AIOCBState aiocb_type);
+static int coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req);
static AIOReq *find_pending_req(BDRVSheepdogState *s, uint64_t oid)
@@ -749,9 +750,19 @@ static void coroutine_fn aio_read_response(void *opaque)
}
}
- if (rsp.result != SD_RES_SUCCESS) {
+ switch (rsp.result) {
+ case SD_RES_SUCCESS:
+ break;
+ case SD_RES_READONLY:
+ ret = resend_aioreq(s, aio_req);
+ if (ret == SD_RES_SUCCESS) {
+ goto out;
+ }
+ /* fall through */
+ default:
acb->ret = -EIO;
error_report("%s", sd_strerror(rsp.result));
+ break;
}
free_aio_req(s, aio_req);
@@ -1186,6 +1197,53 @@ out:
return ret;
}
+static int coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req)
+{
+ SheepdogAIOCB *acb = aio_req->aiocb;
+ bool create = false;
+ int ret;
+
+ ret = reload_inode(s, 0, "");
+ if (ret < 0) {
+ return ret;
+ }
+
+ aio_req->oid = vid_to_data_oid(s->inode.vdi_id,
+ data_oid_to_idx(aio_req->oid));
+
+ /* check whether this request becomes a CoW one */
+ if (acb->aiocb_type == AIOCB_WRITE_UDATA) {
+ int idx = data_oid_to_idx(aio_req->oid);
+ AIOReq *areq;
+
+ if (s->inode.data_vdi_id[idx] == 0) {
+ create = true;
+ goto out;
+ }
+ if (is_data_obj_writable(&s->inode, idx)) {
+ goto out;
+ }
+
+ /* link to the pending list if there is another CoW request to
+ * the same object */
+ QLIST_FOREACH(areq, &s->inflight_aio_head, aio_siblings) {
+ if (areq != aio_req && areq->oid == aio_req->oid) {
+ dprintf("simultaneous CoW to %" PRIx64 "\n", aio_req->oid);
+ QLIST_REMOVE(aio_req, aio_siblings);
+ QLIST_INSERT_HEAD(&s->pending_aio_head, aio_req, aio_siblings);
+ return SD_RES_SUCCESS;
+ }
+ }
+
+ aio_req->base_oid = vid_to_data_oid(s->inode.data_vdi_id[idx], idx);
+ aio_req->flags |= SD_FLAG_CMD_COW;
+ create = true;
+ }
+out:
+ return add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov,
+ create, acb->aiocb_type);
+}
+
/* TODO Convert to fine grained options */
static QemuOptsList runtime_opts = {
.name = "sheepdog",
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 11/11] rbd: add an asynchronous flush
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (19 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 10/11] sheepdog: resend write requests when SD_RES_READONLY is received Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
2013-04-26 11:44 ` [Qemu-devel] [PATCH 11/11] sheepdog: fix loadvm operation Stefan Hajnoczi
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Josh Durgin, Anthony Liguori, Stefan Hajnoczi
From: Josh Durgin <josh.durgin@inktank.com>
The existing bdrv_co_flush_to_disk implementation uses rbd_flush(),
which is sychronous and causes the main qemu thread to block until it
is complete. This results in unresponsiveness and extra latency for
the guest.
Fix this by using an asynchronous version of flush. This was added to
librbd with a special #define to indicate its presence, since it will
be backported to stable versions. Thus, there is no need to check the
version of librbd.
Implement this as bdrv_aio_flush, since it matches other aio functions
in the rbd block driver, and leave out bdrv_co_flush_to_disk when the
asynchronous version is available.
Reported-by: Oliver Francke <oliver@filoo.de>
Signed-off-by: Josh Durgin <josh.durgin@inktank.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/rbd.c | 37 +++++++++++++++++++++++++++++++++----
1 file changed, 33 insertions(+), 4 deletions(-)
diff --git a/block/rbd.c b/block/rbd.c
index 1a8ea6d..141b488 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -63,7 +63,8 @@
typedef enum {
RBD_AIO_READ,
RBD_AIO_WRITE,
- RBD_AIO_DISCARD
+ RBD_AIO_DISCARD,
+ RBD_AIO_FLUSH
} RBDAIOCmd;
typedef struct RBDAIOCB {
@@ -379,8 +380,7 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
r = rcb->ret;
- if (acb->cmd == RBD_AIO_WRITE ||
- acb->cmd == RBD_AIO_DISCARD) {
+ if (acb->cmd != RBD_AIO_READ) {
if (r < 0) {
acb->ret = r;
acb->error = 1;
@@ -659,6 +659,16 @@ static int rbd_aio_discard_wrapper(rbd_image_t image,
#endif
}
+static int rbd_aio_flush_wrapper(rbd_image_t image,
+ rbd_completion_t comp)
+{
+#ifdef LIBRBD_SUPPORTS_AIO_FLUSH
+ return rbd_aio_flush(image, comp);
+#else
+ return -ENOTSUP;
+#endif
+}
+
static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
int64_t sector_num,
QEMUIOVector *qiov,
@@ -679,7 +689,7 @@ static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
acb = qemu_aio_get(&rbd_aiocb_info, bs, cb, opaque);
acb->cmd = cmd;
acb->qiov = qiov;
- if (cmd == RBD_AIO_DISCARD) {
+ if (cmd == RBD_AIO_DISCARD || cmd == RBD_AIO_FLUSH) {
acb->bounce = NULL;
} else {
acb->bounce = qemu_blockalign(bs, qiov->size);
@@ -723,6 +733,9 @@ static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
case RBD_AIO_DISCARD:
r = rbd_aio_discard_wrapper(s->image, off, size, c);
break;
+ case RBD_AIO_FLUSH:
+ r = rbd_aio_flush_wrapper(s->image, c);
+ break;
default:
r = -EINVAL;
}
@@ -762,6 +775,16 @@ static BlockDriverAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
RBD_AIO_WRITE);
}
+#ifdef LIBRBD_SUPPORTS_AIO_FLUSH
+static BlockDriverAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ return rbd_start_aio(bs, 0, NULL, 0, cb, opaque, RBD_AIO_FLUSH);
+}
+
+#else
+
static int qemu_rbd_co_flush(BlockDriverState *bs)
{
#if LIBRBD_VERSION_CODE >= LIBRBD_VERSION(0, 1, 1)
@@ -772,6 +795,7 @@ static int qemu_rbd_co_flush(BlockDriverState *bs)
return 0;
#endif
}
+#endif
static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi)
{
@@ -949,7 +973,12 @@ static BlockDriver bdrv_rbd = {
.bdrv_aio_readv = qemu_rbd_aio_readv,
.bdrv_aio_writev = qemu_rbd_aio_writev,
+
+#ifdef LIBRBD_SUPPORTS_AIO_FLUSH
+ .bdrv_aio_flush = qemu_rbd_aio_flush,
+#else
.bdrv_co_flush_to_disk = qemu_rbd_co_flush,
+#endif
#ifdef LIBRBD_SUPPORTS_DISCARD
.bdrv_aio_discard = qemu_rbd_aio_discard,
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [Qemu-devel] [PATCH 11/11] sheepdog: fix loadvm operation
2013-04-26 11:44 [Qemu-devel] [PULL 00/11] Block patches Stefan Hajnoczi
` (20 preceding siblings ...)
2013-04-26 11:44 ` [Qemu-devel] [PATCH 11/11] rbd: add an asynchronous flush Stefan Hajnoczi
@ 2013-04-26 11:44 ` Stefan Hajnoczi
21 siblings, 0 replies; 51+ messages in thread
From: Stefan Hajnoczi @ 2013-04-26 11:44 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Anthony Liguori, Stefan Hajnoczi, Liu Yuan, MORITA Kazutaka
From: Liu Yuan <tailai.ly@taobao.com>
Currently the 'loadvm' opertaion works as following:
1. switch to the snapshot
2. mark current working VDI as a snapshot
3. rely on sd_create_branch to create a new working VDI based on the snapshot
This works not the same as other format as QCOW2. For e.g,
qemu > savevm # get a live snapshot snap1
qemu > savevm # snap2
qemu > loadvm 1 # This will steally create snap3 of the working VDI
Which will result in following snapshot chain:
base <-- snap1 <-- snap2 <-- snap3
^
|
working VDI
snap3 was unnecessarily created and might be annoying users.
This patch discard the unnecessary 'snap3' creation. and implement
rollback(loadvm) operation to the specified snapshot by
1. switch to the snapshot
2. delete working VDI
3. rely on sd_create_branch to create a new working VDI based on the snapshot
The snapshot chain for above example will be:
base <-- snap1 <-- snap2
^
|
working VDI
Cc: qemu-devel@nongnu.org
Cc: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
Cc: Kevin Wolf <kwolf@redhat.com>
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Liu Yuan <tailai.ly@taobao.com>
Reviewed-by: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/sheepdog.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 53 insertions(+), 1 deletion(-)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 77e21fd..21a4edf 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -36,6 +36,7 @@
#define SD_OP_GET_VDI_INFO 0x14
#define SD_OP_READ_VDIS 0x15
#define SD_OP_FLUSH_VDI 0x16
+#define SD_OP_DEL_VDI 0x17
#define SD_FLAG_CMD_WRITE 0x01
#define SD_FLAG_CMD_COW 0x02
@@ -1666,6 +1667,43 @@ out:
sd_finish_aiocb(acb);
}
+/* Delete current working VDI on the snapshot chain */
+static bool sd_delete(BDRVSheepdogState *s)
+{
+ unsigned int wlen = SD_MAX_VDI_LEN, rlen = 0;
+ SheepdogVdiReq hdr = {
+ .opcode = SD_OP_DEL_VDI,
+ .vdi_id = s->inode.vdi_id,
+ .data_length = wlen,
+ .flags = SD_FLAG_CMD_WRITE,
+ };
+ SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
+ int fd, ret;
+
+ fd = connect_to_sdog(s);
+ if (fd < 0) {
+ return false;
+ }
+
+ ret = do_req(fd, (SheepdogReq *)&hdr, s->name, &wlen, &rlen);
+ closesocket(fd);
+ if (ret) {
+ return false;
+ }
+ switch (rsp->result) {
+ case SD_RES_NO_VDI:
+ error_report("%s was already deleted", s->name);
+ /* fall through */
+ case SD_RES_SUCCESS:
+ break;
+ default:
+ error_report("%s, %s", sd_strerror(rsp->result), s->name);
+ return false;
+ }
+
+ return true;
+}
+
/*
* Create a writable VDI from a snapshot
*/
@@ -1674,12 +1712,20 @@ static int sd_create_branch(BDRVSheepdogState *s)
int ret, fd;
uint32_t vid;
char *buf;
+ bool deleted;
dprintf("%" PRIx32 " is snapshot.\n", s->inode.vdi_id);
buf = g_malloc(SD_INODE_SIZE);
- ret = do_sd_create(s, s->name, s->inode.vdi_size, s->inode.vdi_id, &vid, 1);
+ /*
+ * Even If deletion fails, we will just create extra snapshot based on
+ * the workding VDI which was supposed to be deleted. So no need to
+ * false bail out.
+ */
+ deleted = sd_delete(s);
+ ret = do_sd_create(s, s->name, s->inode.vdi_size, s->inode.vdi_id, &vid,
+ !deleted);
if (ret) {
goto out;
}
@@ -1995,6 +2041,12 @@ cleanup:
return ret;
}
+/*
+ * We implement rollback(loadvm) operation to the specified snapshot by
+ * 1) switch to the snapshot
+ * 2) rely on sd_create_branch to delete working VDI and
+ * 3) create a new working VDI based on the speicified snapshot
+ */
static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
{
BDRVSheepdogState *s = bs->opaque;
--
1.8.1.4
^ permalink raw reply related [flat|nested] 51+ messages in thread