Block pull request
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJTbSUxAAoJEJykq7OBq3PIh+EH/1pfLspteDS4hlTZZ8D5r+iN AEmemUQpMDGawLHQSJcK3xgNWSz5ei3HxLuXz9+5f3ZhP+ECsrTnf+60uzHkdd6j axG1viAHEBtX0ZumTdo0XY6FtCZmCRqRz8nfqxs1Q3O7UtZaDqLf1m/BNguw5K8G VHtuPAVidTWcS6QT6CoEdJ4coA3F8ZuK1viTU2nsBE28lqB99ZG9Zkr2pOCXXra2 5d6OIZYyc+PNW2HuNZTmma41aVoYJnT797qr2cLbZ3q38ykwmWU6cNrLsf+O91yT wnsCG6g1MdQb9mwVp0spPU/X/IuKbRg449XOzY9Ko4HmuSn1Inf6gUIBMigecjQ= =wmRq -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/stefanha/tags/block-pull-request' into staging Block pull request # gpg: Signature made Fri 09 May 2014 19:57:53 BST using RSA key ID 81AB73C8 # gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>" # gpg: aka "Stefan Hajnoczi <stefanha@gmail.com>" * remotes/stefanha/tags/block-pull-request: glib: fix g_poll early timeout on windows block: qemu-iotests - test for live migration block: qemu-iotests - update 085 to use common.qemu block: qemu-iotests - add common.qemu, for bash-controlled qemu tests block/raw-posix: Try both FIEMAP and SEEK_HOLE gluster: Correctly propagate errors when volume isn't accessible vl.c: remove init_clocks call from main block: Fix open flags with BDRV_O_SNAPSHOT qemu-iotests: Test converting to streamOptimized from small cluster size vmdk: Implement .bdrv_get_info() vmdk: Implement .bdrv_write_compressed qemu-img: Convert by cluster size if target is compressed block/iscsi: bump year in copyright notice block/nfs: Check for NULL server part qemu-img: sort block formats in help message iotests: Use configured python qcow2: Fix alloc_clusters_noref() overflow detection Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1b5498f687
34
block.c
34
block.c
@ -774,6 +774,16 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
|
||||
bs->copy_on_read--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the flags that a temporary snapshot should get, based on the
|
||||
* originally requested flags (the originally requested image will have flags
|
||||
* like a backing file)
|
||||
*/
|
||||
static int bdrv_temp_snapshot_flags(int flags)
|
||||
{
|
||||
return (flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the flags that bs->file should get, based on the given flags for
|
||||
* the parent BDS
|
||||
@ -787,11 +797,6 @@ static int bdrv_inherited_flags(int flags)
|
||||
* so we can enable both unconditionally on lower layers. */
|
||||
flags |= BDRV_O_CACHE_WB | BDRV_O_UNMAP;
|
||||
|
||||
/* The backing file of a temporary snapshot is read-only */
|
||||
if (flags & BDRV_O_SNAPSHOT) {
|
||||
flags &= ~BDRV_O_RDWR;
|
||||
}
|
||||
|
||||
/* Clear flags that only apply to the top layer */
|
||||
flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ);
|
||||
|
||||
@ -817,11 +822,6 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
|
||||
{
|
||||
int open_flags = flags | BDRV_O_CACHE_WB;
|
||||
|
||||
/* The backing file of a temporary snapshot is read-only */
|
||||
if (flags & BDRV_O_SNAPSHOT) {
|
||||
open_flags &= ~BDRV_O_RDWR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear flags that are internal to the block layer before opening the
|
||||
* image.
|
||||
@ -1206,7 +1206,7 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bdrv_append_temp_snapshot(BlockDriverState *bs, Error **errp)
|
||||
void bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
|
||||
{
|
||||
/* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
|
||||
char *tmp_filename = g_malloc0(PATH_MAX + 1);
|
||||
@ -1262,8 +1262,7 @@ void bdrv_append_temp_snapshot(BlockDriverState *bs, Error **errp)
|
||||
bs_snapshot = bdrv_new("", &error_abort);
|
||||
|
||||
ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options,
|
||||
(bs->open_flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY,
|
||||
bdrv_qcow2, &local_err);
|
||||
flags, bdrv_qcow2, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
@ -1298,6 +1297,7 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
BlockDriverState *file = NULL, *bs;
|
||||
const char *drvname;
|
||||
Error *local_err = NULL;
|
||||
int snapshot_flags = 0;
|
||||
|
||||
assert(pbs);
|
||||
|
||||
@ -1358,6 +1358,10 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
flags |= BDRV_O_ALLOW_RDWR;
|
||||
}
|
||||
if (flags & BDRV_O_SNAPSHOT) {
|
||||
snapshot_flags = bdrv_temp_snapshot_flags(flags);
|
||||
flags = bdrv_backing_flags(flags);
|
||||
}
|
||||
|
||||
assert(file == NULL);
|
||||
ret = bdrv_open_image(&file, filename, options, "file",
|
||||
@ -1417,8 +1421,8 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
|
||||
/* For snapshot=on, create a temporary qcow2 overlay. bs points to the
|
||||
* temporary snapshot afterwards. */
|
||||
if (flags & BDRV_O_SNAPSHOT) {
|
||||
bdrv_append_temp_snapshot(bs, &local_err);
|
||||
if (snapshot_flags) {
|
||||
bdrv_append_temp_snapshot(bs, snapshot_flags, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto close_and_fail;
|
||||
|
@ -207,6 +207,11 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename,
|
||||
"volume=%s image=%s transport=%s", gconf->server,
|
||||
gconf->port, gconf->volname, gconf->image,
|
||||
gconf->transport);
|
||||
|
||||
/* glfs_init sometimes doesn't set errno although docs suggest that */
|
||||
if (errno == 0)
|
||||
errno = EINVAL;
|
||||
|
||||
goto out;
|
||||
}
|
||||
return glfs;
|
||||
@ -482,7 +487,7 @@ static int qemu_gluster_create(const char *filename,
|
||||
|
||||
glfs = qemu_gluster_init(gconf, filename, errp);
|
||||
if (!glfs) {
|
||||
ret = -EINVAL;
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
* QEMU Block driver for iSCSI images
|
||||
*
|
||||
* Copyright (c) 2010-2011 Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
* Copyright (c) 2012-2013 Peter Lieven <pl@kamp.de>
|
||||
* Copyright (c) 2012-2014 Peter Lieven <pl@kamp.de>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -256,6 +256,10 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename,
|
||||
error_setg(errp, "Invalid URL specified");
|
||||
goto fail;
|
||||
}
|
||||
if (!uri->server) {
|
||||
error_setg(errp, "Invalid URL specified");
|
||||
goto fail;
|
||||
}
|
||||
strp = strrchr(uri->path, '/');
|
||||
if (strp == NULL) {
|
||||
error_setg(errp, "Invalid URL specified");
|
||||
|
@ -656,7 +656,9 @@ retry:
|
||||
|
||||
/* Make sure that all offsets in the "allocated" range are representable
|
||||
* in an int64_t */
|
||||
if (s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits)) {
|
||||
if (s->free_cluster_index > 0 &&
|
||||
s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits))
|
||||
{
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
|
@ -146,6 +146,9 @@ typedef struct BDRVRawState {
|
||||
bool has_discard:1;
|
||||
bool has_write_zeroes:1;
|
||||
bool discard_zeroes:1;
|
||||
#ifdef CONFIG_FIEMAP
|
||||
bool skip_fiemap;
|
||||
#endif
|
||||
} BDRVRawState;
|
||||
|
||||
typedef struct BDRVRawReopenState {
|
||||
@ -1272,6 +1275,83 @@ static int raw_create(const char *filename, QEMUOptionParameter *options,
|
||||
return result;
|
||||
}
|
||||
|
||||
static int64_t try_fiemap(BlockDriverState *bs, off_t start, off_t *data,
|
||||
off_t *hole, int nb_sectors, int *pnum)
|
||||
{
|
||||
#ifdef CONFIG_FIEMAP
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
|
||||
struct {
|
||||
struct fiemap fm;
|
||||
struct fiemap_extent fe;
|
||||
} f;
|
||||
|
||||
if (s->skip_fiemap) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
f.fm.fm_start = start;
|
||||
f.fm.fm_length = (int64_t)nb_sectors * BDRV_SECTOR_SIZE;
|
||||
f.fm.fm_flags = 0;
|
||||
f.fm.fm_extent_count = 1;
|
||||
f.fm.fm_reserved = 0;
|
||||
if (ioctl(s->fd, FS_IOC_FIEMAP, &f) == -1) {
|
||||
s->skip_fiemap = true;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (f.fm.fm_mapped_extents == 0) {
|
||||
/* No extents found, data is beyond f.fm.fm_start + f.fm.fm_length.
|
||||
* f.fm.fm_start + f.fm.fm_length must be clamped to the file size!
|
||||
*/
|
||||
off_t length = lseek(s->fd, 0, SEEK_END);
|
||||
*hole = f.fm.fm_start;
|
||||
*data = MIN(f.fm.fm_start + f.fm.fm_length, length);
|
||||
} else {
|
||||
*data = f.fe.fe_logical;
|
||||
*hole = f.fe.fe_logical + f.fe.fe_length;
|
||||
if (f.fe.fe_flags & FIEMAP_EXTENT_UNWRITTEN) {
|
||||
ret |= BDRV_BLOCK_ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
#else
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int64_t try_seek_hole(BlockDriverState *bs, off_t start, off_t *data,
|
||||
off_t *hole, int *pnum)
|
||||
{
|
||||
#if defined SEEK_HOLE && defined SEEK_DATA
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
*hole = lseek(s->fd, start, SEEK_HOLE);
|
||||
if (*hole == -1) {
|
||||
/* -ENXIO indicates that sector_num was past the end of the file.
|
||||
* There is a virtual hole there. */
|
||||
assert(errno != -ENXIO);
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (*hole > start) {
|
||||
*data = start;
|
||||
} else {
|
||||
/* On a hole. We need another syscall to find its end. */
|
||||
*data = lseek(s->fd, start, SEEK_DATA);
|
||||
if (*data == -1) {
|
||||
*data = lseek(s->fd, 0, SEEK_END);
|
||||
}
|
||||
}
|
||||
|
||||
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
|
||||
#else
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true iff the specified sector is present in the disk image. Drivers
|
||||
* not implementing the functionality are assumed to not support backing files,
|
||||
@ -1288,10 +1368,10 @@ static int raw_create(const char *filename, QEMUOptionParameter *options,
|
||||
* beyond the end of the disk image it will be clamped.
|
||||
*/
|
||||
static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
off_t start, data, hole;
|
||||
off_t start, data = 0, hole = 0;
|
||||
int64_t ret;
|
||||
|
||||
ret = fd_open(bs);
|
||||
@ -1300,71 +1380,18 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
start = sector_num * BDRV_SECTOR_SIZE;
|
||||
ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
|
||||
|
||||
#ifdef CONFIG_FIEMAP
|
||||
|
||||
BDRVRawState *s = bs->opaque;
|
||||
struct {
|
||||
struct fiemap fm;
|
||||
struct fiemap_extent fe;
|
||||
} f;
|
||||
|
||||
f.fm.fm_start = start;
|
||||
f.fm.fm_length = (int64_t)nb_sectors * BDRV_SECTOR_SIZE;
|
||||
f.fm.fm_flags = 0;
|
||||
f.fm.fm_extent_count = 1;
|
||||
f.fm.fm_reserved = 0;
|
||||
if (ioctl(s->fd, FS_IOC_FIEMAP, &f) == -1) {
|
||||
/* Assume everything is allocated. */
|
||||
*pnum = nb_sectors;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (f.fm.fm_mapped_extents == 0) {
|
||||
/* No extents found, data is beyond f.fm.fm_start + f.fm.fm_length.
|
||||
* f.fm.fm_start + f.fm.fm_length must be clamped to the file size!
|
||||
*/
|
||||
off_t length = lseek(s->fd, 0, SEEK_END);
|
||||
hole = f.fm.fm_start;
|
||||
data = MIN(f.fm.fm_start + f.fm.fm_length, length);
|
||||
} else {
|
||||
data = f.fe.fe_logical;
|
||||
hole = f.fe.fe_logical + f.fe.fe_length;
|
||||
if (f.fe.fe_flags & FIEMAP_EXTENT_UNWRITTEN) {
|
||||
ret |= BDRV_BLOCK_ZERO;
|
||||
ret = try_fiemap(bs, start, &data, &hole, nb_sectors, pnum);
|
||||
if (ret < 0) {
|
||||
ret = try_seek_hole(bs, start, &data, &hole, pnum);
|
||||
if (ret < 0) {
|
||||
/* Assume everything is allocated. */
|
||||
data = 0;
|
||||
hole = start + nb_sectors * BDRV_SECTOR_SIZE;
|
||||
ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined SEEK_HOLE && defined SEEK_DATA
|
||||
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
hole = lseek(s->fd, start, SEEK_HOLE);
|
||||
if (hole == -1) {
|
||||
/* -ENXIO indicates that sector_num was past the end of the file.
|
||||
* There is a virtual hole there. */
|
||||
assert(errno != -ENXIO);
|
||||
|
||||
/* Most likely EINVAL. Assume everything is allocated. */
|
||||
*pnum = nb_sectors;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (hole > start) {
|
||||
data = start;
|
||||
} else {
|
||||
/* On a hole. We need another syscall to find its end. */
|
||||
data = lseek(s->fd, start, SEEK_DATA);
|
||||
if (data == -1) {
|
||||
data = lseek(s->fd, 0, SEEK_END);
|
||||
}
|
||||
}
|
||||
#else
|
||||
data = 0;
|
||||
hole = start + nb_sectors * BDRV_SECTOR_SIZE;
|
||||
#endif
|
||||
|
||||
if (data <= start) {
|
||||
/* On a data extent, compute sectors to the end of the extent. */
|
||||
*pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE);
|
||||
|
35
block/vmdk.c
35
block/vmdk.c
@ -1496,6 +1496,19 @@ static coroutine_fn int vmdk_co_write(BlockDriverState *bs, int64_t sector_num,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vmdk_write_compressed(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
const uint8_t *buf,
|
||||
int nb_sectors)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
if (s->num_extents == 1 && s->extents[0].compressed) {
|
||||
return vmdk_write(bs, sector_num, buf, nb_sectors, false, false);
|
||||
} else {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
static int coroutine_fn vmdk_co_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors,
|
||||
@ -2063,6 +2076,26 @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs)
|
||||
return spec_info;
|
||||
}
|
||||
|
||||
static int vmdk_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
int i;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
assert(s->num_extents);
|
||||
bdi->needs_compressed_writes = s->extents[0].compressed;
|
||||
if (!s->extents[0].flat) {
|
||||
bdi->cluster_size = s->extents[0].cluster_sectors << BDRV_SECTOR_BITS;
|
||||
}
|
||||
/* See if we have multiple extents but they have different cases */
|
||||
for (i = 1; i < s->num_extents; i++) {
|
||||
if (bdi->needs_compressed_writes != s->extents[i].compressed ||
|
||||
(bdi->cluster_size && bdi->cluster_size !=
|
||||
s->extents[i].cluster_sectors << BDRV_SECTOR_BITS)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QEMUOptionParameter vmdk_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
@ -2109,6 +2142,7 @@ static BlockDriver bdrv_vmdk = {
|
||||
.bdrv_reopen_prepare = vmdk_reopen_prepare,
|
||||
.bdrv_read = vmdk_co_read,
|
||||
.bdrv_write = vmdk_co_write,
|
||||
.bdrv_write_compressed = vmdk_write_compressed,
|
||||
.bdrv_co_write_zeroes = vmdk_co_write_zeroes,
|
||||
.bdrv_close = vmdk_close,
|
||||
.bdrv_create = vmdk_create,
|
||||
@ -2118,6 +2152,7 @@ static BlockDriver bdrv_vmdk = {
|
||||
.bdrv_has_zero_init = vmdk_has_zero_init,
|
||||
.bdrv_get_specific_info = vmdk_get_specific_info,
|
||||
.bdrv_refresh_limits = vmdk_refresh_limits,
|
||||
.bdrv_get_info = vmdk_get_info,
|
||||
|
||||
.create_options = vmdk_create_options,
|
||||
};
|
||||
|
6
configure
vendored
6
configure
vendored
@ -4771,6 +4771,12 @@ if test "$gcov" = "yes" ; then
|
||||
echo "GCOV=$gcov_tool" >> $config_host_mak
|
||||
fi
|
||||
|
||||
iotests_common_env="tests/qemu-iotests/common.env"
|
||||
|
||||
echo "# Automatically generated by configure - do not modify" > $iotests_common_env
|
||||
echo >> $iotests_common_env
|
||||
echo "PYTHON='$python'" >> $iotests_common_env
|
||||
|
||||
# use included Linux headers
|
||||
if test "$linux" = "yes" ; then
|
||||
mkdir -p linux-headers
|
||||
|
@ -34,6 +34,10 @@ typedef struct BlockDriverInfo {
|
||||
* opened with BDRV_O_UNMAP flag for this to work.
|
||||
*/
|
||||
bool can_write_zeroes_with_unmap;
|
||||
/*
|
||||
* True if this block driver only supports compressed writes
|
||||
*/
|
||||
bool needs_compressed_writes;
|
||||
} BlockDriverInfo;
|
||||
|
||||
typedef struct BlockFragInfo {
|
||||
@ -191,7 +195,7 @@ int bdrv_open_image(BlockDriverState **pbs, const char *filename,
|
||||
QDict *options, const char *bdref_key, int flags,
|
||||
bool allow_none, Error **errp);
|
||||
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp);
|
||||
void bdrv_append_temp_snapshot(BlockDriverState *bs, Error **errp);
|
||||
void bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp);
|
||||
int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
const char *reference, QDict *options, int flags,
|
||||
BlockDriver *drv, Error **errp);
|
||||
|
@ -24,7 +24,14 @@ static inline guint g_timeout_add_seconds(guint interval, GSourceFunc function,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !GLIB_CHECK_VERSION(2, 20, 0)
|
||||
#ifdef _WIN32
|
||||
/*
|
||||
* g_poll has a problem on Windows when using
|
||||
* timeouts < 10ms, so use wrapper.
|
||||
*/
|
||||
#define g_poll(fds, nfds, timeout) g_poll_fixed(fds, nfds, timeout)
|
||||
gint g_poll_fixed(GPollFD *fds, guint nfds, gint timeout);
|
||||
#elif !GLIB_CHECK_VERSION(2, 20, 0)
|
||||
/*
|
||||
* Glib before 2.20.0 doesn't implement g_poll, so wrap it to compile properly
|
||||
* on older systems.
|
||||
|
29
qemu-img.c
29
qemu-img.c
@ -32,6 +32,7 @@
|
||||
#include "block/block_int.h"
|
||||
#include "block/qapi.h"
|
||||
#include <getopt.h>
|
||||
#include <glib.h>
|
||||
|
||||
#define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION \
|
||||
", Copyright (c) 2004-2008 Fabrice Bellard\n"
|
||||
@ -55,9 +56,25 @@ typedef enum OutputFormat {
|
||||
#define BDRV_O_FLAGS BDRV_O_CACHE_WB
|
||||
#define BDRV_DEFAULT_CACHE "writeback"
|
||||
|
||||
static void format_print(void *opaque, const char *name)
|
||||
static gint compare_data(gconstpointer a, gconstpointer b, gpointer user)
|
||||
{
|
||||
printf(" %s", name);
|
||||
return g_strcmp0(a, b);
|
||||
}
|
||||
|
||||
static void print_format(gpointer data, gpointer user)
|
||||
{
|
||||
printf(" %s", (char *)data);
|
||||
}
|
||||
|
||||
static void add_format_to_seq(void *opaque, const char *fmt_name)
|
||||
{
|
||||
GSequence *seq = opaque;
|
||||
|
||||
if (!g_sequence_lookup(seq, (gpointer)fmt_name,
|
||||
compare_data, NULL)) {
|
||||
g_sequence_insert_sorted(seq, (gpointer)fmt_name,
|
||||
compare_data, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void QEMU_NORETURN GCC_FMT_ATTR(1, 2) error_exit(const char *fmt, ...)
|
||||
@ -142,10 +159,15 @@ static void QEMU_NORETURN help(void)
|
||||
" '-f' first image format\n"
|
||||
" '-F' second image format\n"
|
||||
" '-s' run in Strict mode - fail on different image size or sector allocation\n";
|
||||
GSequence *seq;
|
||||
|
||||
printf("%s\nSupported formats:", help_msg);
|
||||
bdrv_iterate_format(format_print, NULL);
|
||||
seq = g_sequence_new(NULL);
|
||||
bdrv_iterate_format(add_format_to_seq, seq);
|
||||
g_sequence_foreach(seq, print_format, NULL);
|
||||
printf("\n");
|
||||
g_sequence_free(seq);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@ -1480,6 +1502,7 @@ static int img_convert(int argc, char **argv)
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
compress = compress || bdi.needs_compressed_writes;
|
||||
cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
|
@ -126,6 +126,9 @@ static void qemu_clock_init(QEMUClockType type)
|
||||
{
|
||||
QEMUClock *clock = qemu_clock_ptr(type);
|
||||
|
||||
/* Assert that the clock of type TYPE has not been initialized yet. */
|
||||
assert(main_loop_tlg.tl[type] == NULL);
|
||||
|
||||
clock->type = type;
|
||||
clock->enabled = true;
|
||||
clock->last = INT64_MIN;
|
||||
|
@ -35,6 +35,7 @@ _cleanup()
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.env
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
. ./common.pattern
|
||||
@ -56,22 +57,22 @@ for IMGOPTS in "compat=0.10" "compat=1.1"; do
|
||||
echo === Create image with unknown header extension ===
|
||||
echo
|
||||
_make_test_img 64M
|
||||
./qcow2.py "$TEST_IMG" add-header-ext 0x12345678 "This is a test header extension"
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" add-header-ext 0x12345678 "This is a test header extension"
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
_check_test_img
|
||||
|
||||
echo
|
||||
echo === Rewrite header with no backing file ===
|
||||
echo
|
||||
$QEMU_IMG rebase -u -b "" "$TEST_IMG"
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
_check_test_img
|
||||
|
||||
echo
|
||||
echo === Add a backing file and format ===
|
||||
echo
|
||||
$QEMU_IMG rebase -u -b "/some/backing/file/path" -F host_device "$TEST_IMG"
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
done
|
||||
|
||||
# success, all done
|
||||
|
@ -38,6 +38,7 @@ _cleanup()
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.env
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
. ./common.pattern
|
||||
@ -53,15 +54,15 @@ IMGOPTS="compat=1.1"
|
||||
echo === Create image with unknown autoclear feature bit ===
|
||||
echo
|
||||
_make_test_img 64M
|
||||
./qcow2.py "$TEST_IMG" set-feature-bit autoclear 63
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" set-feature-bit autoclear 63
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
|
||||
echo
|
||||
echo === Repair image ===
|
||||
echo
|
||||
_check_test_img -r all
|
||||
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
|
@ -38,6 +38,7 @@ _cleanup()
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.env
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
@ -58,7 +59,7 @@ _make_test_img $size
|
||||
$QEMU_IO -c "write -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# The dirty bit must not be set
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
_check_test_img
|
||||
|
||||
echo
|
||||
@ -73,7 +74,7 @@ $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io
|
||||
ulimit -c "$old_ulimit"
|
||||
|
||||
# The dirty bit must be set
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
_check_test_img
|
||||
|
||||
echo
|
||||
@ -82,7 +83,7 @@ echo "== Read-only access must still work =="
|
||||
$QEMU_IO -r -c "read -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# The dirty bit must be set
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
echo
|
||||
echo "== Repairing the image file must succeed =="
|
||||
@ -90,7 +91,7 @@ echo "== Repairing the image file must succeed =="
|
||||
_check_test_img -r all
|
||||
|
||||
# The dirty bit must not be set
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
echo
|
||||
echo "== Data should still be accessible after repair =="
|
||||
@ -109,12 +110,12 @@ $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io
|
||||
ulimit -c "$old_ulimit"
|
||||
|
||||
# The dirty bit must be set
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
$QEMU_IO -c "write 0 512" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# The dirty bit must not be set
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
echo
|
||||
echo "== Creating an image file with lazy_refcounts=off =="
|
||||
@ -128,7 +129,7 @@ $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io
|
||||
ulimit -c "$old_ulimit"
|
||||
|
||||
# The dirty bit must not be set since lazy_refcounts=off
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
_check_test_img
|
||||
|
||||
echo
|
||||
@ -144,8 +145,8 @@ $QEMU_IO -c "write 0 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG commit "$TEST_IMG"
|
||||
|
||||
# The dirty bit must not be set
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
./qcow2.py "$TEST_IMG".base dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG".base dump-header | grep incompatible_features
|
||||
|
||||
_check_test_img
|
||||
TEST_IMG="$TEST_IMG".base _check_test_img
|
||||
|
@ -233,6 +233,10 @@ echo 'qemu-io ide0-hd0 "write -P 0x22 0 4k"' | run_qemu -drive file="$TEST_IMG",
|
||||
|
||||
$QEMU_IO -c "read -P 0x22 0 4k" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo -e 'qemu-io ide0-hd0 "write -P 0x33 0 4k"\ncommit ide0-hd0' | run_qemu -drive file="$TEST_IMG",snapshot=on | _filter_qemu_io
|
||||
|
||||
$QEMU_IO -c "read -P 0x33 0 4k" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
|
@ -356,6 +356,16 @@ wrote 4096/4096 bytes at offset 0
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
read 4096/4096 bytes at offset 0
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqe[K[D[Dqem[K[D[D[Dqemu[K[D[D[D[Dqemu-[K[D[D[D[D[Dqemu-i[K[D[D[D[D[D[Dqemu-io[K[D[D[D[D[D[D[Dqemu-io [K[D[D[D[D[D[D[D[Dqemu-io i[K[D[D[D[D[D[D[D[D[Dqemu-io id[K[D[D[D[D[D[D[D[D[D[Dqemu-io ide[K[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0[K[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-[K[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-h[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "w[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "wr[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "wri[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "writ[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write -[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write -P[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write -P [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write -P 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write -P 0x[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write -P 0x3[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write -P 0x33[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write -P 0x33 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write -P 0x33 0[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write -P 0x33 0 [K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write -P 0x33 0 4[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write -P 0x33 0 4k[K[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[D[Dqemu-io ide0-hd0 "write -P 0x33 0 4k"[K
|
||||
wrote 4096/4096 bytes at offset 0
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
(qemu) c[K[Dco[K[D[Dcom[K[D[D[Dcomm[K[D[D[D[Dcommi[K[D[D[D[D[Dcommit[K[D[D[D[D[D[Dcommit [K[D[D[D[D[D[D[Dcommit i[K[D[D[D[D[D[D[D[Dcommit id[K[D[D[D[D[D[D[D[D[Dcommit ide[K[D[D[D[D[D[D[D[D[D[Dcommit ide0[K[D[D[D[D[D[D[D[D[D[D[Dcommit ide0-[K[D[D[D[D[D[D[D[D[D[D[D[Dcommit ide0-h[K[D[D[D[D[D[D[D[D[D[D[D[D[Dcommit ide0-hd[K[D[D[D[D[D[D[D[D[D[D[D[D[D[Dcommit ide0-hd0[K
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
read 4096/4096 bytes at offset 0
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
*** done
|
||||
|
@ -35,6 +35,7 @@ _cleanup()
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.env
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
@ -49,7 +50,7 @@ _make_test_img $((1024*1024))T
|
||||
echo
|
||||
echo "creating too large image (1 EB) using qcow2.py"
|
||||
_make_test_img 4G
|
||||
./qcow2.py "$TEST_IMG" set-header size $((1024 ** 6))
|
||||
$PYTHON qcow2.py "$TEST_IMG" set-header size $((1024 ** 6))
|
||||
_check_test_img
|
||||
|
||||
# success, all done
|
||||
|
@ -103,6 +103,13 @@ IMGOPTS="subformat=monolithicSparse" _make_test_img 100G
|
||||
truncate -s 10M $TEST_IMG
|
||||
_img_info
|
||||
|
||||
echo
|
||||
echo "=== Converting to streamOptimized from image with small cluster size==="
|
||||
TEST_IMG="$TEST_IMG.qcow2" IMGFMT=qcow2 IMGOPTS="cluster_size=4096" _make_test_img 1G
|
||||
$QEMU_IO -c "write -P 0xa 0 512" "$TEST_IMG.qcow2" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -P 0xb 10240 512" "$TEST_IMG.qcow2" | _filter_qemu_io
|
||||
$QEMU_IMG convert -f qcow2 -O vmdk -o subformat=streamOptimized "$TEST_IMG.qcow2" "$TEST_IMG" 2>&1
|
||||
|
||||
echo
|
||||
echo "=== Testing version 3 ==="
|
||||
_use_sample_img iotest-version3.vmdk.bz2
|
||||
|
@ -2046,10 +2046,18 @@ RW 12582912 VMFS "dummy.IMGFMT" 1
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=107374182400
|
||||
qemu-img: Could not open 'TEST_DIR/t.IMGFMT': File truncated, expecting at least 13172736 bytes
|
||||
|
||||
=== Converting to streamOptimized from image with small cluster size===
|
||||
Formatting 'TEST_DIR/t.vmdk.IMGFMT', fmt=IMGFMT size=1073741824
|
||||
wrote 512/512 bytes at offset 0
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 512/512 bytes at offset 10240
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
=== Testing version 3 ===
|
||||
image: TEST_DIR/iotest-version3.IMGFMT
|
||||
file format: IMGFMT
|
||||
virtual size: 1.0G (1073741824 bytes)
|
||||
cluster_size: 65536
|
||||
|
||||
=== Testing 4TB monolithicFlat creation and IO ===
|
||||
Formatting 'TEST_DIR/iotest-version3.IMGFMT', fmt=IMGFMT size=4398046511104
|
||||
|
@ -35,6 +35,7 @@ _cleanup()
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.env
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
@ -68,13 +69,13 @@ poke_file "$TEST_IMG" "$l1_offset" "\x80\x00\x00\x00\x00\x03\x00\x00"
|
||||
_check_test_img
|
||||
|
||||
# The corrupt bit should not be set anyway
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
# Try to write something, thereby forcing the corrupt bit to be set
|
||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
|
||||
|
||||
# The corrupt bit must now be set
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
# Try to open the image R/W (which should fail)
|
||||
$QEMU_IO -c "$OPEN_RW" -c "read 0 512" 2>&1 | _filter_qemu_io \
|
||||
@ -99,19 +100,19 @@ poke_file "$TEST_IMG" "$(($rb_offset+8))" "\x00\x01"
|
||||
# Redirect new data cluster onto refcount block
|
||||
poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x02\x00\x00"
|
||||
_check_test_img
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
# Try to fix it
|
||||
_check_test_img -r all
|
||||
|
||||
# The corrupt bit should be cleared
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
# Look if it's really really fixed
|
||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
echo
|
||||
echo "=== Testing cluster data reference into inactive L2 table ==="
|
||||
@ -124,13 +125,13 @@ $QEMU_IO -c "$OPEN_RW" -c "write -P 2 0 512" | _filter_qemu_io
|
||||
poke_file "$TEST_IMG" "$l2_offset_after_snapshot" \
|
||||
"\x80\x00\x00\x00\x00\x04\x00\x00"
|
||||
_check_test_img
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 3 0 512" | _filter_qemu_io
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
_check_test_img -r all
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 4 0 512" | _filter_qemu_io
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
# Check data
|
||||
$QEMU_IO -c "$OPEN_RO" -c "read -P 4 0 512" | _filter_qemu_io
|
||||
|
@ -35,6 +35,7 @@ _cleanup()
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.env
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
@ -48,9 +49,9 @@ echo "=== Testing version downgrade with zero expansion ==="
|
||||
echo
|
||||
IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M
|
||||
$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
|
||||
_check_test_img
|
||||
|
||||
@ -59,9 +60,9 @@ echo "=== Testing dirty version downgrade ==="
|
||||
echo
|
||||
IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M
|
||||
$QEMU_IO -c "write -P 0x2a 0 128k" -c flush -c abort "$TEST_IMG" | _filter_qemu_io
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
|
||||
_check_test_img
|
||||
|
||||
@ -69,11 +70,11 @@ echo
|
||||
echo "=== Testing version downgrade with unknown compat/autoclear flags ==="
|
||||
echo
|
||||
IMGOPTS="compat=1.1" _make_test_img 64M
|
||||
./qcow2.py "$TEST_IMG" set-feature-bit compatible 42
|
||||
./qcow2.py "$TEST_IMG" set-feature-bit autoclear 42
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" set-feature-bit compatible 42
|
||||
$PYTHON qcow2.py "$TEST_IMG" set-feature-bit autoclear 42
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
_check_test_img
|
||||
|
||||
echo
|
||||
@ -81,9 +82,9 @@ echo "=== Testing version upgrade and resize ==="
|
||||
echo
|
||||
IMGOPTS="compat=0.10" _make_test_img 64M
|
||||
$QEMU_IO -c "write -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
$QEMU_IMG amend -o "compat=1.1,lazy_refcounts=on,size=128M" "$TEST_IMG"
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
$QEMU_IO -c "read -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
_check_test_img
|
||||
|
||||
@ -92,9 +93,9 @@ echo "=== Testing dirty lazy_refcounts=off ==="
|
||||
echo
|
||||
IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M
|
||||
$QEMU_IO -c "write -P 0x2a 0 128k" -c flush -c abort "$TEST_IMG" | _filter_qemu_io
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
$QEMU_IMG amend -o "lazy_refcounts=off" "$TEST_IMG"
|
||||
./qcow2.py "$TEST_IMG" dump-header
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
|
||||
_check_test_img
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Test for additional information emitted by qemu-img info on qcow2
|
||||
# images
|
||||
|
@ -29,6 +29,7 @@ tmp=/tmp/$$
|
||||
status=1 # failure is the default!
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.env
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
@ -81,7 +82,7 @@ EOF
|
||||
nbd_url="nbd:127.0.0.1:$port:exportname=foo"
|
||||
fi
|
||||
|
||||
./nbd-fault-injector.py $extra_args "127.0.0.1:$port" "$TEST_DIR/nbd-fault-injector.conf" 2>&1 >/dev/null &
|
||||
$PYTHON nbd-fault-injector.py $extra_args "127.0.0.1:$port" "$TEST_DIR/nbd-fault-injector.conf" 2>&1 >/dev/null &
|
||||
wait_for_tcp_port "127.0.0.1:$port"
|
||||
$QEMU_IO -c "read 0 512" "$nbd_url" 2>&1 | _filter_qemu_io | filter_nbd
|
||||
|
||||
|
@ -30,10 +30,6 @@ echo "QA output created by $seq"
|
||||
|
||||
here=`pwd`
|
||||
status=1 # failure is the default!
|
||||
qemu_pid=
|
||||
|
||||
QMP_IN="${TEST_DIR}/qmp-in-$$"
|
||||
QMP_OUT="${TEST_DIR}/qmp-out-$$"
|
||||
|
||||
snapshot_virt0="snapshot-v0.qcow2"
|
||||
snapshot_virt1="snapshot-v1.qcow2"
|
||||
@ -42,10 +38,7 @@ MAX_SNAPSHOTS=10
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
kill -KILL ${qemu_pid}
|
||||
wait ${qemu_pid} 2>/dev/null # silent kill
|
||||
|
||||
rm -f "${QMP_IN}" "${QMP_OUT}"
|
||||
_cleanup_qemu
|
||||
for i in $(seq 1 ${MAX_SNAPSHOTS})
|
||||
do
|
||||
rm -f "${TEST_DIR}/${i}-${snapshot_virt0}"
|
||||
@ -59,43 +52,12 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
. ./common.qemu
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
# Wait for expected QMP response from QEMU. Will time out
|
||||
# after 10 seconds, which counts as failure.
|
||||
#
|
||||
# $1 is the string to expect
|
||||
#
|
||||
# If $silent is set to anything but an empty string, then
|
||||
# response is not echoed out.
|
||||
function timed_wait_for()
|
||||
{
|
||||
while read -t 10 resp <&5
|
||||
do
|
||||
if [ "${silent}" == "" ]; then
|
||||
echo "${resp}" | _filter_testdir | _filter_qemu
|
||||
fi
|
||||
grep -q "${1}" < <(echo ${resp})
|
||||
if [ $? -eq 0 ]; then
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo "Timeout waiting for ${1}"
|
||||
exit 1 # Timeout means the test failed
|
||||
}
|
||||
|
||||
# Sends QMP command to QEMU, and waits for the expected response
|
||||
#
|
||||
# ${1}: String of the QMP command to send
|
||||
# ${2}: String that the QEMU response should contain
|
||||
function send_qmp_cmd()
|
||||
{
|
||||
echo "${1}" >&6
|
||||
timed_wait_for "${2}"
|
||||
}
|
||||
|
||||
# ${1}: unique identifier for the snapshot filename
|
||||
function create_single_snapshot()
|
||||
@ -104,7 +66,7 @@ function create_single_snapshot()
|
||||
'arguments': { 'device': 'virtio0',
|
||||
'snapshot-file':'"${TEST_DIR}/${1}-${snapshot_virt0}"',
|
||||
'format': 'qcow2' } }"
|
||||
send_qmp_cmd "${cmd}" "return"
|
||||
_send_qemu_cmd $h "${cmd}" "return"
|
||||
}
|
||||
|
||||
# ${1}: unique identifier for the snapshot filename
|
||||
@ -120,14 +82,11 @@ function create_group_snapshot()
|
||||
'snapshot-file': '"${TEST_DIR}/${1}-${snapshot_virt1}"' } } ]
|
||||
} }"
|
||||
|
||||
send_qmp_cmd "${cmd}" "return"
|
||||
_send_qemu_cmd $h "${cmd}" "return"
|
||||
}
|
||||
|
||||
size=128M
|
||||
|
||||
mkfifo "${QMP_IN}"
|
||||
mkfifo "${QMP_OUT}"
|
||||
|
||||
_make_test_img $size
|
||||
mv "${TEST_IMG}" "${TEST_IMG}.orig"
|
||||
_make_test_img $size
|
||||
@ -136,23 +95,15 @@ echo
|
||||
echo === Running QEMU ===
|
||||
echo
|
||||
|
||||
"${QEMU}" -nographic -monitor none -serial none -qmp stdio\
|
||||
-drive file="${TEST_IMG}.orig",if=virtio\
|
||||
-drive file="${TEST_IMG}",if=virtio 2>&1 >"${QMP_OUT}" <"${QMP_IN}"&
|
||||
qemu_pid=$!
|
||||
|
||||
# redirect fifos to file descriptors, to keep from blocking
|
||||
exec 5<"${QMP_OUT}"
|
||||
exec 6>"${QMP_IN}"
|
||||
|
||||
# Don't print response, since it has version information in it
|
||||
silent=yes timed_wait_for "capabilities"
|
||||
qemu_comm_method="qmp"
|
||||
_launch_qemu -drive file="${TEST_IMG}.orig",if=virtio -drive file="${TEST_IMG}",if=virtio
|
||||
h=$QEMU_HANDLE
|
||||
|
||||
echo
|
||||
echo === Sending capabilities ===
|
||||
echo
|
||||
|
||||
send_qmp_cmd "{ 'execute': 'qmp_capabilities' }" "return"
|
||||
_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return"
|
||||
|
||||
echo
|
||||
echo === Create a single snapshot on virtio0 ===
|
||||
@ -165,16 +116,16 @@ echo
|
||||
echo === Invalid command - missing device and nodename ===
|
||||
echo
|
||||
|
||||
send_qmp_cmd "{ 'execute': 'blockdev-snapshot-sync',
|
||||
'arguments': { 'snapshot-file':'"${TEST_DIR}"/1-${snapshot_virt0}',
|
||||
_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot-sync',
|
||||
'arguments': { 'snapshot-file':'"${TEST_DIR}/1-${snapshot_virt0}"',
|
||||
'format': 'qcow2' } }" "error"
|
||||
|
||||
echo
|
||||
echo === Invalid command - missing snapshot-file ===
|
||||
echo
|
||||
|
||||
send_qmp_cmd "{ 'execute': 'blockdev-snapshot-sync',
|
||||
'arguments': { 'device': 'virtio0',
|
||||
_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot-sync',
|
||||
'arguments': { 'device': 'virtio0',
|
||||
'format': 'qcow2' } }" "error"
|
||||
echo
|
||||
echo
|
||||
|
105
tests/qemu-iotests/091
Executable file
105
tests/qemu-iotests/091
Executable file
@ -0,0 +1,105 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Live migration test
|
||||
#
|
||||
# Performs a migration from one VM to another via monitor commands
|
||||
#
|
||||
# Copyright (C) 2014 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=jcody@redhat.com
|
||||
|
||||
seq=`basename $0`
|
||||
echo "QA output created by $seq"
|
||||
|
||||
here=`pwd`
|
||||
status=1 # failure is the default!
|
||||
|
||||
MIG_FIFO="${TEST_DIR}/migrate"
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
rm -f "${MIG_FIFO}"
|
||||
_cleanup_qemu
|
||||
_cleanup_test_img
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
. ./common.qemu
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
size=1G
|
||||
|
||||
_make_test_img $size
|
||||
|
||||
mkfifo "${MIG_FIFO}"
|
||||
|
||||
echo
|
||||
echo === Starting QEMU VM1 ===
|
||||
echo
|
||||
|
||||
qemu_comm_method="monitor"
|
||||
_launch_qemu -drive file="${TEST_IMG}",cache=none,id=disk
|
||||
h1=$QEMU_HANDLE
|
||||
|
||||
echo
|
||||
echo === Starting QEMU VM2 ===
|
||||
echo
|
||||
_launch_qemu -drive file="${TEST_IMG}",cache=none,id=disk \
|
||||
-incoming "exec: cat '${MIG_FIFO}'"
|
||||
h2=$QEMU_HANDLE
|
||||
|
||||
echo
|
||||
echo === VM 1: Migrate from VM1 to VM2 ===
|
||||
echo
|
||||
|
||||
silent=yes
|
||||
_send_qemu_cmd $h1 'qemu-io disk "write -P 0x22 0 4M"' "(qemu)"
|
||||
echo "vm1: qemu-io disk write complete"
|
||||
_send_qemu_cmd $h1 "migrate \"exec: cat > '${MIG_FIFO}'\"" "(qemu)"
|
||||
echo "vm1: live migration started"
|
||||
qemu_cmd_repeat=20 _send_qemu_cmd $h1 "info migrate" "completed"
|
||||
echo "vm1: live migration completed"
|
||||
|
||||
echo
|
||||
echo === VM 2: Post-migration, write to disk, verify running ===
|
||||
echo
|
||||
|
||||
_send_qemu_cmd $h2 'qemu-io disk "write 4M 1M"' "(qemu)"
|
||||
echo "vm2: qemu-io disk write complete"
|
||||
qemu_cmd_repeat=20 _send_qemu_cmd $h2 "info status" "running"
|
||||
echo "vm2: qemu process running successfully"
|
||||
|
||||
echo "vm2: flush io, and quit"
|
||||
_send_qemu_cmd $h2 'qemu-io disk flush' "(qemu)"
|
||||
_send_qemu_cmd $h2 'quit' ""
|
||||
|
||||
echo "Check image pattern"
|
||||
${QEMU_IO} -c "read -P 0x22 0 4M" "${TEST_IMG}" | _filter_testdir | _filter_qemu_io
|
||||
|
||||
echo "Running 'qemu-img check -r all \$TEST_IMG'"
|
||||
"${QEMU_IMG}" check -r all "${TEST_IMG}" 2>&1 | _filter_testdir | _filter_qemu
|
||||
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
status=0
|
28
tests/qemu-iotests/091.out
Normal file
28
tests/qemu-iotests/091.out
Normal file
@ -0,0 +1,28 @@
|
||||
QA output created by 091
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||
|
||||
=== Starting QEMU VM1 ===
|
||||
|
||||
|
||||
=== Starting QEMU VM2 ===
|
||||
|
||||
|
||||
=== VM 1: Migrate from VM1 to VM2 ===
|
||||
|
||||
vm1: qemu-io disk write complete
|
||||
vm1: live migration started
|
||||
vm1: live migration completed
|
||||
|
||||
=== VM 2: Post-migration, write to disk, verify running ===
|
||||
|
||||
vm2: qemu-io disk write complete
|
||||
vm2: qemu process running successfully
|
||||
vm2: flush io, and quit
|
||||
Check image pattern
|
||||
read 4194304/4194304 bytes at offset 0
|
||||
4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
Running 'qemu-img check -r all $TEST_IMG'
|
||||
No errors were found on the image.
|
||||
80/16384 = 0.49% allocated, 0.00% fragmented, 0.00% compressed clusters
|
||||
Image end offset: 5570560
|
||||
*** done
|
@ -34,6 +34,13 @@ timestamp=${TIMESTAMP:=false}
|
||||
# generic initialization
|
||||
iam=check
|
||||
|
||||
# we need common.env
|
||||
if ! . ./common.env
|
||||
then
|
||||
echo "$iam: failed to source common.env"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# we need common.config
|
||||
if ! . ./common.config
|
||||
then
|
||||
@ -215,9 +222,16 @@ do
|
||||
|
||||
start=`_wallclock`
|
||||
$timestamp && echo -n " ["`date "+%T"`"]"
|
||||
[ ! -x $seq ] && chmod u+x $seq # ensure we can run it
|
||||
|
||||
if [ "$(head -n 1 $seq)" == "#!/usr/bin/env python" ]; then
|
||||
run_command="$PYTHON $seq"
|
||||
else
|
||||
[ ! -x $seq ] && chmod u+x $seq # ensure we can run it
|
||||
run_command="./$seq"
|
||||
fi
|
||||
|
||||
MALLOC_PERTURB_=${MALLOC_PERTURB_:-$(($RANDOM % 255 + 1))} \
|
||||
./$seq >$tmp.out 2>&1
|
||||
$run_command >$tmp.out 2>&1
|
||||
sts=$?
|
||||
$timestamp && _timestamp
|
||||
stop=`_wallclock`
|
||||
|
200
tests/qemu-iotests/common.qemu
Normal file
200
tests/qemu-iotests/common.qemu
Normal file
@ -0,0 +1,200 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This allows for launching of multiple QEMU instances, with independent
|
||||
# communication possible to each instance.
|
||||
#
|
||||
# Each instance can choose, at launch, to use either the QMP or the
|
||||
# HMP (monitor) interface.
|
||||
#
|
||||
# All instances are cleaned up via _cleanup_qemu, including killing the
|
||||
# running qemu instance.
|
||||
#
|
||||
# Copyright (C) 2014 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/>.
|
||||
#
|
||||
|
||||
QEMU_COMM_TIMEOUT=10
|
||||
|
||||
QEMU_FIFO_IN="${TEST_DIR}/qmp-in-$$"
|
||||
QEMU_FIFO_OUT="${TEST_DIR}/qmp-out-$$"
|
||||
|
||||
QEMU_PID=
|
||||
_QEMU_HANDLE=0
|
||||
QEMU_HANDLE=0
|
||||
|
||||
# If bash version is >= 4.1, these will be overwritten and dynamic
|
||||
# file descriptor values assigned.
|
||||
_out_fd=3
|
||||
_in_fd=4
|
||||
|
||||
# Wait for expected QMP response from QEMU. Will time out
|
||||
# after 10 seconds, which counts as failure.
|
||||
#
|
||||
# Override QEMU_COMM_TIMEOUT for a timeout different than the
|
||||
# default 10 seconds
|
||||
#
|
||||
# $1: The handle to use
|
||||
# $2+ All remaining arguments comprise the string to search for
|
||||
# in the response.
|
||||
#
|
||||
# If $silent is set to anything but an empty string, then
|
||||
# response is not echoed out.
|
||||
function _timed_wait_for()
|
||||
{
|
||||
local h=${1}
|
||||
shift
|
||||
|
||||
QEMU_STATUS[$h]=0
|
||||
while read -t ${QEMU_COMM_TIMEOUT} resp <&${QEMU_OUT[$h]}
|
||||
do
|
||||
if [ -z "${silent}" ]; then
|
||||
echo "${resp}" | _filter_testdir | _filter_qemu \
|
||||
| _filter_qemu_io | _filter_qmp
|
||||
fi
|
||||
grep -q "${*}" < <(echo ${resp})
|
||||
if [ $? -eq 0 ]; then
|
||||
return
|
||||
fi
|
||||
done
|
||||
QEMU_STATUS[$h]=-1
|
||||
if [ -z "${qemu_error_no_exit}" ]; then
|
||||
echo "Timeout waiting for ${*} on handle ${h}"
|
||||
exit 1 # Timeout means the test failed
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Sends QMP or HMP command to QEMU, and waits for the expected response
|
||||
#
|
||||
# $1: QEMU handle to use
|
||||
# $2: String of the QMP command to send
|
||||
# ${@: -1} (Last string passed)
|
||||
# String that the QEMU response should contain. If it is a null
|
||||
# string, do not wait for a response
|
||||
#
|
||||
# Set qemu_cmd_repeat to the number of times to repeat the cmd
|
||||
# until either timeout, or a response. If it is not set, or <=0,
|
||||
# then the command is only sent once.
|
||||
#
|
||||
# If $qemu_error_no_exit is set, then even if the expected response
|
||||
# is not seen, we will not exit. $QEMU_STATUS[$1] will be set it -1 in
|
||||
# that case.
|
||||
function _send_qemu_cmd()
|
||||
{
|
||||
local h=${1}
|
||||
local count=1
|
||||
local cmd=
|
||||
local use_error=${qemu_error_no_exit}
|
||||
shift
|
||||
|
||||
if [ ${qemu_cmd_repeat} -gt 0 ] 2>/dev/null; then
|
||||
count=${qemu_cmd_repeat}
|
||||
use_error="no"
|
||||
fi
|
||||
# This array element extraction is done to accomodate pathnames with spaces
|
||||
cmd=${@: 1:${#@}-1}
|
||||
shift $(($# - 1))
|
||||
|
||||
while [ ${count} -gt 0 ]
|
||||
do
|
||||
echo "${cmd}" >&${QEMU_IN[${h}]}
|
||||
if [ -n "${1}" ]; then
|
||||
qemu_error_no_exit=${use_error} _timed_wait_for ${h} "${1}"
|
||||
if [ ${QEMU_STATUS[$h]} -eq 0 ]; then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
let count--;
|
||||
done
|
||||
if [ ${QEMU_STATUS[$h]} -ne 0 ] && [ -z "${qemu_error_no_exit}" ]; then
|
||||
echo "Timeout waiting for ${1} on handle ${h}"
|
||||
exit 1 #Timeout means the test failed
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Launch a QEMU process.
|
||||
#
|
||||
# Input parameters:
|
||||
# $qemu_comm_method: set this variable to 'monitor' (case insensitive)
|
||||
# to use the QEMU HMP monitor for communication.
|
||||
# Otherwise, the default of QMP is used.
|
||||
# Returns:
|
||||
# $QEMU_HANDLE: set to a handle value to communicate with this QEMU instance.
|
||||
#
|
||||
function _launch_qemu()
|
||||
{
|
||||
local comm=
|
||||
local fifo_out=
|
||||
local fifo_in=
|
||||
|
||||
if (shopt -s nocasematch; [[ "${qemu_comm_method}" == "monitor" ]])
|
||||
then
|
||||
comm="-monitor stdio"
|
||||
else
|
||||
local qemu_comm_method="qmp"
|
||||
comm="-monitor none -qmp stdio"
|
||||
fi
|
||||
|
||||
fifo_out=${QEMU_FIFO_OUT}_${_QEMU_HANDLE}
|
||||
fifo_in=${QEMU_FIFO_IN}_${_QEMU_HANDLE}
|
||||
mkfifo "${fifo_out}"
|
||||
mkfifo "${fifo_in}"
|
||||
|
||||
"${QEMU}" -nographic -serial none ${comm} -machine accel=qtest "${@}" 2>&1 \
|
||||
>"${fifo_out}" \
|
||||
<"${fifo_in}" &
|
||||
QEMU_PID[${_QEMU_HANDLE}]=$!
|
||||
|
||||
if [[ "${BASH_VERSINFO[0]}" -ge "5" ||
|
||||
("${BASH_VERSINFO[0]}" -ge "4" && "${BASH_VERSINFO[1]}" -ge "1") ]]
|
||||
then
|
||||
# bash >= 4.1 required for automatic fd
|
||||
exec {_out_fd}<"${fifo_out}"
|
||||
exec {_in_fd}>"${fifo_in}"
|
||||
else
|
||||
let _out_fd++
|
||||
let _in_fd++
|
||||
eval "exec ${_out_fd}<'${fifo_out}'"
|
||||
eval "exec ${_in_fd}>'${fifo_in}'"
|
||||
fi
|
||||
|
||||
QEMU_OUT[${_QEMU_HANDLE}]=${_out_fd}
|
||||
QEMU_IN[${_QEMU_HANDLE}]=${_in_fd}
|
||||
QEMU_STATUS[${_QEMU_HANDLE}]=0
|
||||
|
||||
if [ "${qemu_comm_method}" == "qmp" ]
|
||||
then
|
||||
# Don't print response, since it has version information in it
|
||||
silent=yes _timed_wait_for ${_QEMU_HANDLE} "capabilities"
|
||||
fi
|
||||
QEMU_HANDLE=${_QEMU_HANDLE}
|
||||
let _QEMU_HANDLE++
|
||||
}
|
||||
|
||||
|
||||
# Silenty kills the QEMU process
|
||||
function _cleanup_qemu()
|
||||
{
|
||||
# QEMU_PID[], QEMU_IN[], QEMU_OUT[] all use same indices
|
||||
for i in "${!QEMU_OUT[@]}"
|
||||
do
|
||||
kill -KILL ${QEMU_PID[$i]} 2>/dev/null
|
||||
wait ${QEMU_PID[$i]} 2>/dev/null # silent kill
|
||||
rm -f "${QEMU_FIFO_IN}_${i}" "${QEMU_FIFO_OUT}_${i}"
|
||||
eval "exec ${QEMU_IN[$i]}<&-" # close file descriptors
|
||||
eval "exec ${QEMU_OUT[$i]}<&-"
|
||||
done
|
||||
}
|
@ -96,3 +96,4 @@
|
||||
087 rw auto
|
||||
088 rw auto
|
||||
090 rw auto quick
|
||||
091 rw auto
|
||||
|
@ -238,3 +238,115 @@ char *qemu_get_exec_dir(void)
|
||||
{
|
||||
return g_strdup(exec_dir);
|
||||
}
|
||||
|
||||
/*
|
||||
* g_poll has a problem on Windows when using
|
||||
* timeouts < 10ms, in glib/gpoll.c:
|
||||
*
|
||||
* // If not, and we have a significant timeout, poll again with
|
||||
* // timeout then. Note that this will return indication for only
|
||||
* // one event, or only for messages. We ignore timeouts less than
|
||||
* // ten milliseconds as they are mostly pointless on Windows, the
|
||||
* // MsgWaitForMultipleObjectsEx() call will timeout right away
|
||||
* // anyway.
|
||||
*
|
||||
* if (retval == 0 && (timeout == INFINITE || timeout >= 10))
|
||||
* retval = poll_rest (poll_msgs, handles, nhandles, fds, nfds, timeout);
|
||||
*
|
||||
* So whenever g_poll is called with timeout < 10ms it does
|
||||
* a quick poll instead of wait, this causes significant performance
|
||||
* degradation of QEMU, thus we should use WaitForMultipleObjectsEx
|
||||
* directly
|
||||
*/
|
||||
gint g_poll_fixed(GPollFD *fds, guint nfds, gint timeout)
|
||||
{
|
||||
guint i;
|
||||
HANDLE handles[MAXIMUM_WAIT_OBJECTS];
|
||||
gint nhandles = 0;
|
||||
int num_completed = 0;
|
||||
|
||||
for (i = 0; i < nfds; i++) {
|
||||
gint j;
|
||||
|
||||
if (fds[i].fd <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* don't add same handle several times
|
||||
*/
|
||||
for (j = 0; j < nhandles; j++) {
|
||||
if (handles[j] == (HANDLE)fds[i].fd) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j == nhandles) {
|
||||
if (nhandles == MAXIMUM_WAIT_OBJECTS) {
|
||||
fprintf(stderr, "Too many handles to wait for!\n");
|
||||
break;
|
||||
} else {
|
||||
handles[nhandles++] = (HANDLE)fds[i].fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < nfds; ++i) {
|
||||
fds[i].revents = 0;
|
||||
}
|
||||
|
||||
if (timeout == -1) {
|
||||
timeout = INFINITE;
|
||||
}
|
||||
|
||||
if (nhandles == 0) {
|
||||
if (timeout == INFINITE) {
|
||||
return -1;
|
||||
} else {
|
||||
SleepEx(timeout, TRUE);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
DWORD res;
|
||||
gint j;
|
||||
|
||||
res = WaitForMultipleObjectsEx(nhandles, handles, FALSE,
|
||||
timeout, TRUE);
|
||||
|
||||
if (res == WAIT_FAILED) {
|
||||
for (i = 0; i < nfds; ++i) {
|
||||
fds[i].revents = 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
} else if ((res == WAIT_TIMEOUT) || (res == WAIT_IO_COMPLETION) ||
|
||||
((int)res < (int)WAIT_OBJECT_0) ||
|
||||
(res >= (WAIT_OBJECT_0 + nhandles))) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < nfds; ++i) {
|
||||
if (handles[res - WAIT_OBJECT_0] == (HANDLE)fds[i].fd) {
|
||||
fds[i].revents = fds[i].events;
|
||||
}
|
||||
}
|
||||
|
||||
++num_completed;
|
||||
|
||||
if (nhandles <= 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* poll the rest of the handles
|
||||
*/
|
||||
for (j = res - WAIT_OBJECT_0 + 1; j < nhandles; j++) {
|
||||
handles[j - 1] = handles[j];
|
||||
}
|
||||
--nhandles;
|
||||
|
||||
timeout = 0;
|
||||
}
|
||||
|
||||
return num_completed;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user