Block patches for 2.3

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJU/uuVAAoJEH8JsnLIjy/WULwP/jeARjYkFuG3ahSWpeY0JnTK
 QCkLF06iSQQUiirXI4H+Tofl8kNVBd/Iinv+LbkF27iWbTiwalmLz7NiyboX8dl+
 NJZtCrqp44q7KFbl3g19/jop/zdZ9N5Gxp8BARVUILHQb1y5cXJwrDhBxTmNRDL+
 sSZXfomCgKtMP40nGLa0CcNIYKlm8MePJEM2TsMoWv7tYz4CXgBG39EqK6NJluCY
 kTTMcbdrLbR0imfKOVPutCgV8rhRXJ0oGVD3Q+D3/LFmPG++hoRnWCcDm6ZZ62Hi
 Ra7u87TBfAUUtiT+vFQJnd7hTpN+stQidsCDBLEY3qPTKYhzm648PHvcEwOAv6YW
 sjAELF2Rrsbe4vkL3/qgYDusnaPMElrHVEdbKtHofWtg6KctLnYIhusV+qKq1Fpa
 cRQEbQIZMVFeWN1G9WuYH8RBYrwJqp+/qq7DcnV62lUAdY4e3iO7E3yMLFDwpxku
 PLl7eofU/ZpnAOrrU2QAQvgXZRqy1ie/Unv8jFwefQkK5mXHoCtkAeBlOM8t4kJf
 HjkC/hYO7kwPdaz6xK80wpXqYd3vT6jKi7mlJqC5oQQLGJbRigxlMZ16UIAx+IrL
 NxhnQChp7IP21KMATFbpvYjcJyGMw3ZuVRaUhQBgqQArIomVHvM5WcN9M6S5dsmj
 vClFOIqjlSbtsmChceWr
 =hlbC
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block patches for 2.3

# gpg: Signature made Tue Mar 10 13:03:17 2015 GMT using RSA key ID C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"

* remotes/kevin/tags/for-upstream: (73 commits)
  MAINTAINERS: Add jcody as blockjobs, block devices maintainer
  iotests: add O_DIRECT alignment probing test
  block/raw-posix: fix launching with failed disks
  MAINTAINERS: Add jsnow as IDE maintainer
  sheepdog: Fix misleading error messages in sd_snapshot_create()
  Add testcase for scsi-hd devices without drive property
  scsi-hd: fix property unset case
  block/vdi: Add locking for parallel requests
  iotests: Drop vpc from 004's and 104's format list
  iotests: Remove 006
  iotests: Fix 051's reference output
  virtio-blk: Remove the stale FIXME comment
  tests: Check QVIRTIO_F_ANY_LAYOUT flag in virtio-blk test
  libqos: Solve bug in interrupt checking when using MSIX in virtio-pci.c
  sheepdog: fix confused return values
  qtest/ahci: add fragmented dma test
  qtest/ahci: Add PIO and LBA48 tests
  qtest/ahci: Add DMA test variants
  libqos/ahci: add ahci command helpers
  qtest/ahci: Add a macro bootup routine
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2015-03-10 14:01:22 +00:00
commit 1976058109
85 changed files with 2693 additions and 840 deletions

View File

@ -605,11 +605,25 @@ S: Maintained
F: hw/misc/edu.c
IDE
M: Kevin Wolf <kwolf@redhat.com>
M: Stefan Hajnoczi <stefanha@redhat.com>
S: Odd Fixes
M: John Snow <jsnow@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: include/hw/ide.h
F: hw/ide/
F: hw/block/block.c
F: hw/block/cdrom.c
F: hw/block/hd-geometry.c
F: tests/ide-test.c
F: tests/ahci-test.c
T: git git://github.com/jnsnow/qemu.git ide
Floppy
M: John Snow <jsnow@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: hw/block/fdc.c
F: include/hw/block/fdc.h
T: git git://github.com/jnsnow/qemu.git ide
OMAP
M: Peter Maydell <peter.maydell@linaro.org>
@ -770,6 +784,18 @@ F: tests/qemu-iotests/
T: git git://repo.or.cz/qemu/kevin.git block
T: git git://github.com/stefanha/qemu.git block
Block Jobs
M: Jeff Cody <jcody@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: blockjob.c
F: include/block/blockjob.h
F: block/backup.c
F: block/commit.c
F: block/stream.h
F: block/mirror.c
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
Character Devices
M: Anthony Liguori <aliguori@amazon.com>
M: Paolo Bonzini <pbonzini@redhat.com>
@ -1079,20 +1105,28 @@ F: block/vmdk.c
RBD
M: Josh Durgin <josh.durgin@inktank.com>
M: Jeff Cody <jcody@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: block/rbd.c
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
Sheepdog
M: Hitoshi Mitake <mitake.hitoshi@lab.ntt.co.jp>
M: Liu Yuan <namei.unix@gmail.com>
M: Jeff Cody <jcody@redhat.com>
L: qemu-block@nongnu.org
L: sheepdog@lists.wpkg.org
S: Supported
F: block/sheepdog.c
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
VHDX
M: Jeff Cody <jcody@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: block/vhdx*
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
VDI
M: Stefan Weil <sw@weilnetz.de>
@ -1107,19 +1141,42 @@ S: Supported
F: block/iscsi.c
NFS
M: Jeff Cody <jcody@redhat.com>
M: Peter Lieven <pl@kamp.de>
L: qemu-block@nongnu.org
S: Maintained
F: block/nfs.c
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
SSH
M: Richard W.M. Jones <rjones@redhat.com>
M: Jeff Cody <jcody@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: block/ssh.c
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
ARCHIPELAGO
M: Chrysostomos Nanakos <chris@include.gr>
M: Jeff Cody <jcody@redhat.com>
L: qemu-block@nongnu.org
S: Maintained
F: block/archipelago.c
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
CURL
M: Jeff Cody <jcody@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: block/curl.c
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
GLUSTER
M: Jeff Cody <jcody@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: block/gluster.c
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
Bootdevice
M: Gonglei <arei.gonglei@huawei.com>

34
block.c
View File

@ -568,6 +568,40 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
}
}
/**
* Try to get @bs's logical and physical block size.
* On success, store them in @bsz struct and return 0.
* On failure return -errno.
* @bs must not be empty.
*/
int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz)
{
BlockDriver *drv = bs->drv;
if (drv && drv->bdrv_probe_blocksizes) {
return drv->bdrv_probe_blocksizes(bs, bsz);
}
return -ENOTSUP;
}
/**
* Try to get @bs's geometry (cyls, heads, sectors).
* On success, store them in @geo struct and return 0.
* On failure return -errno.
* @bs must not be empty.
*/
int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
{
BlockDriver *drv = bs->drv;
if (drv && drv->bdrv_probe_geometry) {
return drv->bdrv_probe_geometry(bs, geo);
}
return -ENOTSUP;
}
/*
* Create a uniquely-named empty temporary file.
* Return 0 upon success, otherwise a negative errno value.

View File

@ -472,12 +472,14 @@ static BlockAIOCB *inject_error(BlockDriverState *bs,
int error = rule->options.inject.error;
struct BlkdebugAIOCB *acb;
QEMUBH *bh;
bool immediately = rule->options.inject.immediately;
if (rule->options.inject.once) {
QSIMPLEQ_INIT(&s->active_rules);
QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next);
remove_rule(rule);
}
if (rule->options.inject.immediately) {
if (immediately) {
return NULL;
}

View File

@ -892,3 +892,13 @@ int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size)
{
return bdrv_load_vmstate(blk->bs, buf, pos, size);
}
int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz)
{
return bdrv_probe_blocksizes(blk->bs, bsz);
}
int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo)
{
return bdrv_probe_geometry(blk->bs, geo);
}

View File

@ -1640,7 +1640,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
for (i = 0; i < l1_size; i++) {
uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK;
bool l2_dirty = false;
int l2_refcount;
uint64_t l2_refcount;
if (!l2_offset) {
/* unallocated */
@ -1672,9 +1672,9 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
goto fail;
}
l2_refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits);
if (l2_refcount < 0) {
ret = l2_refcount;
ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits,
&l2_refcount);
if (ret < 0) {
goto fail;
}
@ -1707,7 +1707,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
/* For shared L2 tables, set the refcount accordingly (it is
* already 1 and needs to be l2_refcount) */
ret = qcow2_update_cluster_refcount(bs,
offset >> s->cluster_bits, l2_refcount - 1,
offset >> s->cluster_bits,
refcount_diff(1, l2_refcount), false,
QCOW2_DISCARD_OTHER);
if (ret < 0) {
qcow2_free_clusters(bs, offset, s->cluster_size,

File diff suppressed because it is too large Load Diff

View File

@ -677,13 +677,16 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
}
/* Check support for various header values */
if (header.refcount_order != 4) {
report_unsupported(bs, errp, "%d bit reference counts",
1 << header.refcount_order);
ret = -ENOTSUP;
if (header.refcount_order > 6) {
error_setg(errp, "Reference count entry width too large; may not "
"exceed 64 bits");
ret = -EINVAL;
goto fail;
}
s->refcount_order = header.refcount_order;
s->refcount_bits = 1 << s->refcount_order;
s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1);
s->refcount_max += s->refcount_max - 1;
if (header.crypt_method > QCOW_CRYPT_AES) {
error_setg(errp, "Unsupported encryption method: %" PRIu32,
@ -1780,7 +1783,7 @@ static int preallocate(BlockDriverState *bs)
static int qcow2_create2(const char *filename, int64_t total_size,
const char *backing_file, const char *backing_format,
int flags, size_t cluster_size, PreallocMode prealloc,
QemuOpts *opts, int version,
QemuOpts *opts, int version, int refcount_order,
Error **errp)
{
/* Calculate cluster_bits */
@ -1813,9 +1816,21 @@ static int qcow2_create2(const char *filename, int64_t total_size,
int ret;
if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) {
/* Note: The following calculation does not need to be exact; if it is a
* bit off, either some bytes will be "leaked" (which is fine) or we
* will need to increase the file size by some bytes (which is fine,
* too, as long as the bulk is allocated here). Therefore, using
* floating point arithmetic is fine. */
int64_t meta_size = 0;
uint64_t nreftablee, nrefblocke, nl1e, nl2e;
int64_t aligned_total_size = align_offset(total_size, cluster_size);
int refblock_bits, refblock_size;
/* refcount entry size in bytes */
double rces = (1 << refcount_order) / 8.;
/* see qcow2_open() */
refblock_bits = cluster_bits - (refcount_order - 3);
refblock_size = 1 << refblock_bits;
/* header: 1 cluster */
meta_size += cluster_size;
@ -1840,20 +1855,20 @@ static int qcow2_create2(const char *filename, int64_t total_size,
* c = cluster size
* y1 = number of refcount blocks entries
* y2 = meta size including everything
* rces = refcount entry size in bytes
* then,
* y1 = (y2 + a)/c
* y2 = y1 * sizeof(u16) + y1 * sizeof(u16) * sizeof(u64) / c + m
* y2 = y1 * rces + y1 * rces * sizeof(u64) / c + m
* we can get y1:
* y1 = (a + m) / (c - sizeof(u16) - sizeof(u16) * sizeof(u64) / c)
* y1 = (a + m) / (c - rces - rces * sizeof(u64) / c)
*/
nrefblocke = (aligned_total_size + meta_size + cluster_size) /
(cluster_size - sizeof(uint16_t) -
1.0 * sizeof(uint16_t) * sizeof(uint64_t) / cluster_size);
nrefblocke = align_offset(nrefblocke, cluster_size / sizeof(uint16_t));
meta_size += nrefblocke * sizeof(uint16_t);
nrefblocke = (aligned_total_size + meta_size + cluster_size)
/ (cluster_size - rces - rces * sizeof(uint64_t)
/ cluster_size);
meta_size += DIV_ROUND_UP(nrefblocke, refblock_size) * cluster_size;
/* total size of refcount tables */
nreftablee = nrefblocke * sizeof(uint16_t) / cluster_size;
nreftablee = nrefblocke / refblock_size;
nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t));
meta_size += nreftablee * sizeof(uint64_t);
@ -1889,7 +1904,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
.l1_size = cpu_to_be32(0),
.refcount_table_offset = cpu_to_be64(cluster_size),
.refcount_table_clusters = cpu_to_be32(1),
.refcount_order = cpu_to_be32(4),
.refcount_order = cpu_to_be32(refcount_order),
.header_length = cpu_to_be32(sizeof(*header)),
};
@ -2008,6 +2023,8 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp)
size_t cluster_size = DEFAULT_CLUSTER_SIZE;
PreallocMode prealloc;
int version = 3;
uint64_t refcount_bits = 16;
int refcount_order;
Error *local_err = NULL;
int ret;
@ -2062,8 +2079,28 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp)
goto finish;
}
refcount_bits = qemu_opt_get_number_del(opts, BLOCK_OPT_REFCOUNT_BITS,
refcount_bits);
if (refcount_bits > 64 || !is_power_of_2(refcount_bits)) {
error_setg(errp, "Refcount width must be a power of two and may not "
"exceed 64 bits");
ret = -EINVAL;
goto finish;
}
if (version < 3 && refcount_bits != 16) {
error_setg(errp, "Different refcount widths than 16 bits require "
"compatibility level 1.1 or above (use compat=1.1 or "
"greater)");
ret = -EINVAL;
goto finish;
}
refcount_order = ffs(refcount_bits) - 1;
ret = qcow2_create2(filename, size, backing_file, backing_fmt, flags,
cluster_size, prealloc, opts, version, &local_err);
cluster_size, prealloc, opts, version, refcount_order,
&local_err);
if (local_err) {
error_propagate(errp, local_err);
}
@ -2479,7 +2516,8 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
};
if (s->qcow_version == 2) {
*spec_info->qcow2 = (ImageInfoSpecificQCow2){
.compat = g_strdup("0.10"),
.compat = g_strdup("0.10"),
.refcount_bits = s->refcount_bits,
};
} else if (s->qcow_version == 3) {
*spec_info->qcow2 = (ImageInfoSpecificQCow2){
@ -2490,6 +2528,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
.corrupt = s->incompatible_features &
QCOW2_INCOMPAT_CORRUPT,
.has_corrupt = true,
.refcount_bits = s->refcount_bits,
};
}
@ -2642,8 +2681,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
continue;
}
if (!strcmp(desc->name, "compat")) {
compat = qemu_opt_get(opts, "compat");
if (!strcmp(desc->name, BLOCK_OPT_COMPAT_LEVEL)) {
compat = qemu_opt_get(opts, BLOCK_OPT_COMPAT_LEVEL);
if (!compat) {
/* preserve default */
} else if (!strcmp(compat, "0.10")) {
@ -2654,33 +2693,37 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
fprintf(stderr, "Unknown compatibility level %s.\n", compat);
return -EINVAL;
}
} else if (!strcmp(desc->name, "preallocation")) {
} else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) {
fprintf(stderr, "Cannot change preallocation mode.\n");
return -ENOTSUP;
} else if (!strcmp(desc->name, "size")) {
new_size = qemu_opt_get_size(opts, "size", 0);
} else if (!strcmp(desc->name, "backing_file")) {
backing_file = qemu_opt_get(opts, "backing_file");
} else if (!strcmp(desc->name, "backing_fmt")) {
backing_format = qemu_opt_get(opts, "backing_fmt");
} else if (!strcmp(desc->name, "encryption")) {
encrypt = qemu_opt_get_bool(opts, "encryption", s->crypt_method);
} else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) {
new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0);
} else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FILE)) {
backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE);
} else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FMT)) {
backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
} else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) {
encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT,
s->crypt_method);
if (encrypt != !!s->crypt_method) {
fprintf(stderr, "Changing the encryption flag is not "
"supported.\n");
return -ENOTSUP;
}
} else if (!strcmp(desc->name, "cluster_size")) {
cluster_size = qemu_opt_get_size(opts, "cluster_size",
} else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
cluster_size);
if (cluster_size != s->cluster_size) {
fprintf(stderr, "Changing the cluster size is not "
"supported.\n");
return -ENOTSUP;
}
} else if (!strcmp(desc->name, "lazy_refcounts")) {
lazy_refcounts = qemu_opt_get_bool(opts, "lazy_refcounts",
} else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS,
lazy_refcounts);
} else if (!strcmp(desc->name, BLOCK_OPT_REFCOUNT_BITS)) {
error_report("Cannot change refcount entry width");
return -ENOTSUP;
} else {
/* if this assertion fails, this probably means a new option was
* added without having it covered here */
@ -2850,6 +2893,12 @@ static QemuOptsList qcow2_create_opts = {
.help = "Postpone refcount updates",
.def_value_str = "off"
},
{
.name = BLOCK_OPT_REFCOUNT_BITS,
.type = QEMU_OPT_NUMBER,
.help = "Width of a reference count entry in bits",
.def_value_str = "16"
},
{ /* end of list */ }
}
};

View File

@ -213,6 +213,11 @@ typedef struct Qcow2DiscardRegion {
QTAILQ_ENTRY(Qcow2DiscardRegion) next;
} Qcow2DiscardRegion;
typedef uint64_t Qcow2GetRefcountFunc(const void *refcount_array,
uint64_t index);
typedef void Qcow2SetRefcountFunc(void *refcount_array,
uint64_t index, uint64_t value);
typedef struct BDRVQcowState {
int cluster_bits;
int cluster_size;
@ -258,6 +263,11 @@ typedef struct BDRVQcowState {
int qcow_version;
bool use_lazy_refcounts;
int refcount_order;
int refcount_bits;
uint64_t refcount_max;
Qcow2GetRefcountFunc *get_refcount;
Qcow2SetRefcountFunc *set_refcount;
bool discard_passthrough[QCOW2_DISCARD_MAX];
@ -275,17 +285,6 @@ typedef struct BDRVQcowState {
bool cache_discards;
} BDRVQcowState;
/* XXX: use std qcow open function ? */
typedef struct QCowCreateState {
int cluster_size;
int cluster_bits;
uint16_t *refcount_block;
uint64_t *refcount_table;
int64_t l1_table_offset;
int64_t refcount_table_offset;
int64_t refcount_block_offset;
} QCowCreateState;
struct QCowAIOCB;
typedef struct Qcow2COWRegion {
@ -468,6 +467,11 @@ static inline uint64_t l2meta_cow_end(QCowL2Meta *m)
+ (m->cow_end.nb_sectors << BDRV_SECTOR_BITS);
}
static inline uint64_t refcount_diff(uint64_t r1, uint64_t r2)
{
return r1 > r2 ? r1 - r2 : r2 - r1;
}
// FIXME Need qcow2_ prefix to global functions
/* qcow2.c functions */
@ -487,10 +491,12 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
int qcow2_refcount_init(BlockDriverState *bs);
void qcow2_refcount_close(BlockDriverState *bs);
int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index);
int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index,
uint64_t *refcount);
int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index,
int addend, enum qcow2_discard_type type);
uint64_t addend, bool decrease,
enum qcow2_discard_type type);
int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size);
int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,

View File

@ -56,6 +56,10 @@
#include <linux/cdrom.h>
#include <linux/fd.h>
#include <linux/fs.h>
#include <linux/hdreg.h>
#ifdef __s390__
#include <asm/dasd.h>
#endif
#ifndef FS_NOCOW_FL
#define FS_NOCOW_FL 0x00800000 /* Do not cow file */
#endif
@ -218,11 +222,85 @@ static int raw_normalize_devicepath(const char **filename)
}
#endif
/*
* Get logical block size via ioctl. On success store it in @sector_size_p.
*/
static int probe_logical_blocksize(int fd, unsigned int *sector_size_p)
{
unsigned int sector_size;
bool success = false;
errno = ENOTSUP;
/* Try a few ioctls to get the right size */
#ifdef BLKSSZGET
if (ioctl(fd, BLKSSZGET, &sector_size) >= 0) {
*sector_size_p = sector_size;
success = true;
}
#endif
#ifdef DKIOCGETBLOCKSIZE
if (ioctl(fd, DKIOCGETBLOCKSIZE, &sector_size) >= 0) {
*sector_size_p = sector_size;
success = true;
}
#endif
#ifdef DIOCGSECTORSIZE
if (ioctl(fd, DIOCGSECTORSIZE, &sector_size) >= 0) {
*sector_size_p = sector_size;
success = true;
}
#endif
return success ? 0 : -errno;
}
/**
* Get physical block size of @fd.
* On success, store it in @blk_size and return 0.
* On failure, return -errno.
*/
static int probe_physical_blocksize(int fd, unsigned int *blk_size)
{
#ifdef BLKPBSZGET
if (ioctl(fd, BLKPBSZGET, blk_size) < 0) {
return -errno;
}
return 0;
#else
return -ENOTSUP;
#endif
}
/* Check if read is allowed with given memory buffer and length.
*
* This function is used to check O_DIRECT memory buffer and request alignment.
*/
static bool raw_is_io_aligned(int fd, void *buf, size_t len)
{
ssize_t ret = pread(fd, buf, len, 0);
if (ret >= 0) {
return true;
}
#ifdef __linux__
/* The Linux kernel returns EINVAL for misaligned O_DIRECT reads. Ignore
* other errors (e.g. real I/O error), which could happen on a failed
* drive, since we only care about probing alignment.
*/
if (errno != EINVAL) {
return true;
}
#endif
return false;
}
static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp)
{
BDRVRawState *s = bs->opaque;
char *buf;
unsigned int sector_size;
/* For /dev/sg devices the alignment is not really used.
With buffered I/O, we don't have any restrictions. */
@ -232,25 +310,12 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp)
return;
}
/* Try a few ioctls to get the right size */
bs->request_alignment = 0;
s->buf_align = 0;
#ifdef BLKSSZGET
if (ioctl(fd, BLKSSZGET, &sector_size) >= 0) {
bs->request_alignment = sector_size;
/* Let's try to use the logical blocksize for the alignment. */
if (probe_logical_blocksize(fd, &bs->request_alignment) < 0) {
bs->request_alignment = 0;
}
#endif
#ifdef DKIOCGETBLOCKSIZE
if (ioctl(fd, DKIOCGETBLOCKSIZE, &sector_size) >= 0) {
bs->request_alignment = sector_size;
}
#endif
#ifdef DIOCGSECTORSIZE
if (ioctl(fd, DIOCGSECTORSIZE, &sector_size) >= 0) {
bs->request_alignment = sector_size;
}
#endif
#ifdef CONFIG_XFS
if (s->is_xfs) {
struct dioattr da;
@ -267,7 +332,7 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp)
size_t align;
buf = qemu_memalign(MAX_BLOCKSIZE, 2 * MAX_BLOCKSIZE);
for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) {
if (pread(fd, buf + align, MAX_BLOCKSIZE, 0) >= 0) {
if (raw_is_io_aligned(fd, buf + align, MAX_BLOCKSIZE)) {
s->buf_align = align;
break;
}
@ -279,7 +344,7 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp)
size_t align;
buf = qemu_memalign(s->buf_align, MAX_BLOCKSIZE);
for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) {
if (pread(fd, buf, align, 0) >= 0) {
if (raw_is_io_aligned(fd, buf, align)) {
bs->request_alignment = align;
break;
}
@ -655,6 +720,86 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
bs->bl.opt_mem_alignment = s->buf_align;
}
static int check_for_dasd(int fd)
{
#ifdef BIODASDINFO2
struct dasd_information2_t info = {0};
return ioctl(fd, BIODASDINFO2, &info);
#else
return -1;
#endif
}
/**
* Try to get @bs's logical and physical block size.
* On success, store them in @bsz and return zero.
* On failure, return negative errno.
*/
static int hdev_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz)
{
BDRVRawState *s = bs->opaque;
int ret;
/* If DASD, get blocksizes */
if (check_for_dasd(s->fd) < 0) {
return -ENOTSUP;
}
ret = probe_logical_blocksize(s->fd, &bsz->log);
if (ret < 0) {
return ret;
}
return probe_physical_blocksize(s->fd, &bsz->phys);
}
/**
* Try to get @bs's geometry: cyls, heads, sectors.
* On success, store them in @geo and return 0.
* On failure return -errno.
* (Allows block driver to assign default geometry values that guest sees)
*/
#ifdef __linux__
static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
{
BDRVRawState *s = bs->opaque;
struct hd_geometry ioctl_geo = {0};
uint32_t blksize;
/* If DASD, get its geometry */
if (check_for_dasd(s->fd) < 0) {
return -ENOTSUP;
}
if (ioctl(s->fd, HDIO_GETGEO, &ioctl_geo) < 0) {
return -errno;
}
/* HDIO_GETGEO may return success even though geo contains zeros
(e.g. certain multipath setups) */
if (!ioctl_geo.heads || !ioctl_geo.sectors || !ioctl_geo.cylinders) {
return -ENOTSUP;
}
/* Do not return a geometry for partition */
if (ioctl_geo.start != 0) {
return -ENOTSUP;
}
geo->heads = ioctl_geo.heads;
geo->sectors = ioctl_geo.sectors;
if (!probe_physical_blocksize(s->fd, &blksize)) {
/* overwrite cyls: HDIO_GETGEO result is incorrect for big drives */
geo->cylinders = bdrv_nb_sectors(bs) / (blksize / BDRV_SECTOR_SIZE)
/ (geo->heads * geo->sectors);
return 0;
}
geo->cylinders = ioctl_geo.cylinders;
return 0;
}
#else /* __linux__ */
static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
{
return -ENOTSUP;
}
#endif
static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb)
{
int ret;
@ -944,7 +1089,9 @@ static ssize_t handle_aiocb_write_zeroes_block(RawPosixAIOData *aiocb)
static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb)
{
#if defined(CONFIG_FALLOCATE) || defined(CONFIG_XFS)
BDRVRawState *s = aiocb->bs->opaque;
#endif
if (aiocb->aio_type & QEMU_AIO_BLKDEV) {
return handle_aiocb_write_zeroes_block(aiocb);
@ -2194,6 +2341,8 @@ static BlockDriver bdrv_host_device = {
.bdrv_get_info = raw_get_info,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
.bdrv_probe_blocksizes = hdev_probe_blocksizes,
.bdrv_probe_geometry = hdev_probe_geometry,
.bdrv_detach_aio_context = raw_detach_aio_context,
.bdrv_attach_aio_context = raw_attach_aio_context,

View File

@ -235,6 +235,16 @@ static int raw_probe(const uint8_t *buf, int buf_size, const char *filename)
return 1;
}
static int raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz)
{
return bdrv_probe_blocksizes(bs->file, bsz);
}
static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
{
return bdrv_probe_geometry(bs->file, geo);
}
BlockDriver bdrv_raw = {
.format_name = "raw",
.bdrv_probe = &raw_probe,
@ -252,6 +262,8 @@ BlockDriver bdrv_raw = {
.has_variable_length = true,
.bdrv_get_info = &raw_get_info,
.bdrv_refresh_limits = &raw_refresh_limits,
.bdrv_probe_blocksizes = &raw_probe_blocksizes,
.bdrv_probe_geometry = &raw_probe_geometry,
.bdrv_is_inserted = &raw_is_inserted,
.bdrv_media_changed = &raw_media_changed,
.bdrv_eject = &raw_eject,

View File

@ -37,6 +37,7 @@
#define SD_OP_READ_VDIS 0x15
#define SD_OP_FLUSH_VDI 0x16
#define SD_OP_DEL_VDI 0x17
#define SD_OP_GET_CLUSTER_DEFAULT 0x18
#define SD_FLAG_CMD_WRITE 0x01
#define SD_FLAG_CMD_COW 0x02
@ -91,6 +92,7 @@
#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 SD_DEFAULT_BLOCK_SIZE_SHIFT 22
/*
* For erasure coding, we use at most SD_EC_MAX_STRIP for data strips and
* (SD_EC_MAX_STRIP - 1) for parity strips
@ -167,7 +169,8 @@ typedef struct SheepdogVdiReq {
uint32_t base_vdi_id;
uint8_t copies;
uint8_t copy_policy;
uint8_t reserved[2];
uint8_t store_policy;
uint8_t block_size_shift;
uint32_t snapid;
uint32_t type;
uint32_t pad[2];
@ -186,6 +189,21 @@ typedef struct SheepdogVdiRsp {
uint32_t pad[5];
} SheepdogVdiRsp;
typedef struct SheepdogClusterRsp {
uint8_t proto_ver;
uint8_t opcode;
uint16_t flags;
uint32_t epoch;
uint32_t id;
uint32_t data_length;
uint32_t result;
uint8_t nr_copies;
uint8_t copy_policy;
uint8_t block_size_shift;
uint8_t __pad1;
uint32_t __pad2[6];
} SheepdogClusterRsp;
typedef struct SheepdogInode {
char name[SD_MAX_VDI_LEN];
char tag[SD_MAX_VDI_TAG_LEN];
@ -527,6 +545,7 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
return acb;
}
/* Return -EIO in case of error, file descriptor on success */
static int connect_to_sdog(BDRVSheepdogState *s, Error **errp)
{
int fd;
@ -546,11 +565,14 @@ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp)
if (fd >= 0) {
qemu_set_nonblock(fd);
} else {
fd = -EIO;
}
return fd;
}
/* Return 0 on success and -errno in case of error */
static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data,
unsigned int *wlen)
{
@ -559,11 +581,13 @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data,
ret = qemu_co_send(sockfd, hdr, sizeof(*hdr));
if (ret != sizeof(*hdr)) {
error_report("failed to send a req, %s", strerror(errno));
ret = -socket_error();
return ret;
}
ret = qemu_co_send(sockfd, data, *wlen);
if (ret != *wlen) {
ret = -socket_error();
error_report("failed to send a req, %s", strerror(errno));
}
@ -638,6 +662,11 @@ out:
srco->finished = true;
}
/*
* Send the request to the sheep in a synchronous manner.
*
* Return 0 on success, -errno in case of error.
*/
static int do_req(int sockfd, AioContext *aio_context, SheepdogReq *hdr,
void *data, unsigned int *wlen, unsigned int *rlen)
{
@ -1541,6 +1570,7 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot,
hdr.vdi_size = s->inode.vdi_size;
hdr.copy_policy = s->inode.copy_policy;
hdr.copies = s->inode.nr_copies;
hdr.block_size_shift = s->inode.block_size_shift;
ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, buf, &wlen, &rlen);
@ -1566,9 +1596,12 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot,
static int sd_prealloc(const char *filename, Error **errp)
{
BlockDriverState *bs = NULL;
BDRVSheepdogState *base = NULL;
unsigned long buf_size;
uint32_t idx, max_idx;
uint32_t object_size;
int64_t vdi_size;
void *buf = g_malloc0(SD_DATA_OBJ_SIZE);
void *buf = NULL;
int ret;
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
@ -1582,18 +1615,24 @@ static int sd_prealloc(const char *filename, Error **errp)
ret = vdi_size;
goto out;
}
max_idx = DIV_ROUND_UP(vdi_size, SD_DATA_OBJ_SIZE);
base = bs->opaque;
object_size = (UINT32_C(1) << base->inode.block_size_shift);
buf_size = MIN(object_size, SD_DATA_OBJ_SIZE);
buf = g_malloc0(buf_size);
max_idx = DIV_ROUND_UP(vdi_size, buf_size);
for (idx = 0; idx < max_idx; idx++) {
/*
* The created image can be a cloned image, so we need to read
* a data from the source image.
*/
ret = bdrv_pread(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE);
ret = bdrv_pread(bs, idx * buf_size, buf, buf_size);
if (ret < 0) {
goto out;
}
ret = bdrv_pwrite(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE);
ret = bdrv_pwrite(bs, idx * buf_size, buf, buf_size);
if (ret < 0) {
goto out;
}
@ -1666,6 +1705,27 @@ static int parse_redundancy(BDRVSheepdogState *s, const char *opt)
return 0;
}
static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt)
{
struct SheepdogInode *inode = &s->inode;
uint64_t object_size;
int obj_order;
object_size = qemu_opt_get_size_del(opt, BLOCK_OPT_OBJECT_SIZE, 0);
if (object_size) {
if ((object_size - 1) & object_size) { /* not a power of 2? */
return -EINVAL;
}
obj_order = ffs(object_size) - 1;
if (obj_order < 20 || obj_order > 31) {
return -EINVAL;
}
inode->block_size_shift = (uint8_t)obj_order;
}
return 0;
}
static int sd_create(const char *filename, QemuOpts *opts,
Error **errp)
{
@ -1676,6 +1736,7 @@ static int sd_create(const char *filename, QemuOpts *opts,
BDRVSheepdogState *s;
char tag[SD_MAX_VDI_TAG_LEN];
uint32_t snapid;
uint64_t max_vdi_size;
bool prealloc = false;
s = g_new0(BDRVSheepdogState, 1);
@ -1714,10 +1775,11 @@ static int sd_create(const char *filename, QemuOpts *opts,
goto out;
}
}
if (s->inode.vdi_size > SD_MAX_VDI_SIZE) {
error_setg(errp, "too big image size");
ret = -EINVAL;
ret = parse_block_size_shift(s, opts);
if (ret < 0) {
error_setg(errp, "Invalid object_size."
" obect_size needs to be power of 2"
" and be limited from 2^20 to 2^31");
goto out;
}
@ -1754,6 +1816,51 @@ static int sd_create(const char *filename, QemuOpts *opts,
}
s->aio_context = qemu_get_aio_context();
/* if block_size_shift is not specified, get cluster default value */
if (s->inode.block_size_shift == 0) {
SheepdogVdiReq hdr;
SheepdogClusterRsp *rsp = (SheepdogClusterRsp *)&hdr;
Error *local_err = NULL;
int fd;
unsigned int wlen = 0, rlen = 0;
fd = connect_to_sdog(s, &local_err);
if (fd < 0) {
error_report("%s", error_get_pretty(local_err));
error_free(local_err);
ret = -EIO;
goto out;
}
memset(&hdr, 0, sizeof(hdr));
hdr.opcode = SD_OP_GET_CLUSTER_DEFAULT;
hdr.proto_ver = SD_PROTO_VER;
ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr,
NULL, &wlen, &rlen);
closesocket(fd);
if (ret) {
error_setg_errno(errp, -ret, "failed to get cluster default");
goto out;
}
if (rsp->result == SD_RES_SUCCESS) {
s->inode.block_size_shift = rsp->block_size_shift;
} else {
s->inode.block_size_shift = SD_DEFAULT_BLOCK_SIZE_SHIFT;
}
}
max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS;
if (s->inode.vdi_size > max_vdi_size) {
error_setg(errp, "An image is too large."
" The maximum image size is %"PRIu64 "GB",
max_vdi_size / 1024 / 1024 / 1024);
ret = -EINVAL;
goto out;
}
ret = do_sd_create(s, &vid, 0, errp);
if (ret) {
goto out;
@ -1823,11 +1930,13 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset)
BDRVSheepdogState *s = bs->opaque;
int ret, fd;
unsigned int datalen;
uint64_t max_vdi_size;
max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS;
if (offset < s->inode.vdi_size) {
error_report("shrinking is not supported");
return -EINVAL;
} else if (offset > SD_MAX_VDI_SIZE) {
} else if (offset > max_vdi_size) {
error_report("too big image size");
return -EINVAL;
}
@ -2005,9 +2114,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 * BDRV_SECTOR_SIZE;
unsigned long idx = acb->sector_num * BDRV_SECTOR_SIZE / SD_DATA_OBJ_SIZE;
unsigned long idx;
uint32_t object_size;
uint64_t oid;
uint64_t offset = (acb->sector_num * BDRV_SECTOR_SIZE) % SD_DATA_OBJ_SIZE;
uint64_t offset;
BDRVSheepdogState *s = acb->common.bs->opaque;
SheepdogInode *inode = &s->inode;
AIOReq *aio_req;
@ -2024,6 +2134,10 @@ static int coroutine_fn sd_co_rw_vector(void *p)
}
}
object_size = (UINT32_C(1) << inode->block_size_shift);
idx = acb->sector_num * BDRV_SECTOR_SIZE / object_size;
offset = (acb->sector_num * BDRV_SECTOR_SIZE) % object_size;
/*
* Make sure we don't free the aiocb before we are done with all requests.
* This additional reference is dropped at the end of this function.
@ -2037,7 +2151,7 @@ static int coroutine_fn sd_co_rw_vector(void *p)
oid = vid_to_data_oid(inode->data_vdi_id[idx], idx);
len = MIN(total - done, SD_DATA_OBJ_SIZE - offset);
len = MIN(total - done, object_size - offset);
switch (acb->aiocb_type) {
case AIOCB_READ_UDATA:
@ -2061,7 +2175,7 @@ static int coroutine_fn sd_co_rw_vector(void *p)
* 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) {
if (len != object_size || inode->data_vdi_id[idx] == 0) {
goto done;
}
break;
@ -2225,9 +2339,8 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
ret = do_sd_create(s, &new_vid, 1, &local_err);
if (ret < 0) {
error_report_err(local_err);
error_report("failed to create inode for snapshot. %s",
strerror(errno));
error_report("failed to create inode for snapshot: %s",
error_get_pretty(local_err));
goto cleanup;
}
@ -2414,6 +2527,7 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
uint64_t offset;
uint32_t vdi_index;
uint32_t vdi_id = load ? s->inode.parent_vdi_id : s->inode.vdi_id;
uint32_t object_size = (UINT32_C(1) << s->inode.block_size_shift);
fd = connect_to_sdog(s, &local_err);
if (fd < 0) {
@ -2422,10 +2536,10 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
}
while (remaining) {
vdi_index = pos / SD_DATA_OBJ_SIZE;
offset = pos % SD_DATA_OBJ_SIZE;
vdi_index = pos / object_size;
offset = pos % object_size;
data_len = MIN(remaining, SD_DATA_OBJ_SIZE - offset);
data_len = MIN(remaining, object_size - offset);
vmstate_oid = vid_to_vmstate_oid(vdi_id, vdi_index);
@ -2512,10 +2626,11 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
{
BDRVSheepdogState *s = bs->opaque;
SheepdogInode *inode = &s->inode;
uint32_t object_size = (UINT32_C(1) << inode->block_size_shift);
uint64_t offset = sector_num * BDRV_SECTOR_SIZE;
unsigned long start = offset / SD_DATA_OBJ_SIZE,
unsigned long start = offset / object_size,
end = DIV_ROUND_UP((sector_num + nb_sectors) *
BDRV_SECTOR_SIZE, SD_DATA_OBJ_SIZE);
BDRV_SECTOR_SIZE, object_size);
unsigned long idx;
int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset;
@ -2534,7 +2649,7 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
}
}
*pnum = (idx - start) * SD_DATA_OBJ_SIZE / BDRV_SECTOR_SIZE;
*pnum = (idx - start) * object_size / BDRV_SECTOR_SIZE;
if (*pnum > nb_sectors) {
*pnum = nb_sectors;
}
@ -2545,14 +2660,15 @@ static int64_t sd_get_allocated_file_size(BlockDriverState *bs)
{
BDRVSheepdogState *s = bs->opaque;
SheepdogInode *inode = &s->inode;
unsigned long i, last = DIV_ROUND_UP(inode->vdi_size, SD_DATA_OBJ_SIZE);
uint32_t object_size = (UINT32_C(1) << inode->block_size_shift);
unsigned long i, last = DIV_ROUND_UP(inode->vdi_size, object_size);
uint64_t size = 0;
for (i = 0; i < last; i++) {
if (inode->data_vdi_id[i] == 0) {
continue;
}
size += SD_DATA_OBJ_SIZE;
size += object_size;
}
return size;
}
@ -2581,6 +2697,11 @@ static QemuOptsList sd_create_opts = {
.type = QEMU_OPT_STRING,
.help = "Redundancy of the image"
},
{
.name = BLOCK_OPT_OBJECT_SIZE,
.type = QEMU_OPT_SIZE,
.help = "Object size of the image"
},
{ /* end of list */ }
}
};

View File

@ -53,6 +53,7 @@
#include "block/block_int.h"
#include "qemu/module.h"
#include "migration/migration.h"
#include "block/coroutine.h"
#if defined(CONFIG_UUID)
#include <uuid/uuid.h>
@ -196,6 +197,8 @@ typedef struct {
/* VDI header (converted to host endianness). */
VdiHeader header;
CoMutex write_lock;
Error *migration_blocker;
} BDRVVdiState;
@ -504,6 +507,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
"vdi", bdrv_get_device_name(bs), "live migration");
migrate_add_blocker(s->migration_blocker);
qemu_co_mutex_init(&s->write_lock);
return 0;
fail_free_bmap:
@ -639,11 +644,31 @@ static int vdi_co_write(BlockDriverState *bs,
buf, n_sectors * SECTOR_SIZE);
memset(block + (sector_in_block + n_sectors) * SECTOR_SIZE, 0,
(s->block_sectors - n_sectors - sector_in_block) * SECTOR_SIZE);
/* Note that this coroutine does not yield anywhere from reading the
* bmap entry until here, so in regards to all the coroutines trying
* to write to this cluster, the one doing the allocation will
* always be the first to try to acquire the lock.
* Therefore, it is also the first that will actually be able to
* acquire the lock and thus the padded cluster is written before
* the other coroutines can write to the affected area. */
qemu_co_mutex_lock(&s->write_lock);
ret = bdrv_write(bs->file, offset, block, s->block_sectors);
qemu_co_mutex_unlock(&s->write_lock);
} else {
uint64_t offset = s->header.offset_data / SECTOR_SIZE +
(uint64_t)bmap_entry * s->block_sectors +
sector_in_block;
qemu_co_mutex_lock(&s->write_lock);
/* This lock is only used to make sure the following write operation
* is executed after the write issued by the coroutine allocating
* this cluster, therefore we do not need to keep it locked.
* As stated above, the allocating coroutine will always try to lock
* the mutex before all the other concurrent accesses to that
* cluster, therefore at this point we can be absolutely certain
* that that write operation has returned (there may be other writes
* in flight, but they do not concern this very operation). */
qemu_co_mutex_unlock(&s->write_lock);
ret = bdrv_write(bs->file, offset, buf, n_sectors);
}

View File

@ -597,6 +597,51 @@ static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num,
return ret;
}
static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int *pnum)
{
BDRVVPCState *s = bs->opaque;
VHDFooter *footer = (VHDFooter*) s->footer_buf;
int64_t start, offset, next;
bool allocated;
int n;
if (be32_to_cpu(footer->type) == VHD_FIXED) {
*pnum = nb_sectors;
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA |
(sector_num << BDRV_SECTOR_BITS);
}
offset = get_sector_offset(bs, sector_num, 0);
start = offset;
allocated = (offset != -1);
*pnum = 0;
do {
/* All sectors in a block are contiguous (without using the bitmap) */
n = ROUND_UP(sector_num + 1, s->block_size / BDRV_SECTOR_SIZE)
- sector_num;
n = MIN(n, nb_sectors);
*pnum += n;
sector_num += n;
nb_sectors -= n;
next = start + (*pnum * BDRV_SECTOR_SIZE);
if (nb_sectors == 0) {
break;
}
offset = get_sector_offset(bs, sector_num, 0);
} while ((allocated && offset == next) || (!allocated && offset == -1));
if (allocated) {
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
} else {
return 0;
}
}
/*
* Calculates the number of cylinders, heads and sectors per cylinder
* based on a given number of sectors. This is the algorithm described
@ -801,6 +846,7 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
}
total_sectors = (int64_t) cyls * heads * secs_per_cyl;
total_size = total_sectors * BDRV_SECTOR_SIZE;
/* Prepare the Hard Disk Footer */
memset(buf, 0, 1024);
@ -822,13 +868,8 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
/* Version of Virtual PC 2007 */
footer->major = cpu_to_be16(0x0005);
footer->minor = cpu_to_be16(0x0003);
if (disk_type == VHD_DYNAMIC) {
footer->orig_size = cpu_to_be64(total_sectors * 512);
footer->size = cpu_to_be64(total_sectors * 512);
} else {
footer->orig_size = cpu_to_be64(total_size);
footer->size = cpu_to_be64(total_size);
}
footer->orig_size = cpu_to_be64(total_size);
footer->size = cpu_to_be64(total_size);
footer->cyls = cpu_to_be16(cyls);
footer->heads = heads;
footer->secs_per_cyl = secs_per_cyl;
@ -907,8 +948,9 @@ static BlockDriver bdrv_vpc = {
.bdrv_reopen_prepare = vpc_reopen_prepare,
.bdrv_create = vpc_create,
.bdrv_read = vpc_co_read,
.bdrv_write = vpc_co_write,
.bdrv_read = vpc_co_read,
.bdrv_write = vpc_co_write,
.bdrv_co_get_block_status = vpc_co_get_block_status,
.bdrv_get_info = vpc_get_info,

View File

@ -25,6 +25,30 @@ void blkconf_serial(BlockConf *conf, char **serial)
}
}
void blkconf_blocksizes(BlockConf *conf)
{
BlockBackend *blk = conf->blk;
BlockSizes blocksizes;
int backend_ret;
backend_ret = blk_probe_blocksizes(blk, &blocksizes);
/* fill in detected values if they are not defined via qemu command line */
if (!conf->physical_block_size) {
if (!backend_ret) {
conf->physical_block_size = blocksizes.phys;
} else {
conf->physical_block_size = BDRV_SECTOR_SIZE;
}
}
if (!conf->logical_block_size) {
if (!backend_ret) {
conf->logical_block_size = blocksizes.log;
} else {
conf->logical_block_size = BDRV_SECTOR_SIZE;
}
}
}
void blkconf_geometry(BlockConf *conf, int *ptrans,
unsigned cyls_max, unsigned heads_max, unsigned secs_max,
Error **errp)

View File

@ -121,8 +121,16 @@ void hd_geometry_guess(BlockBackend *blk,
int *ptrans)
{
int cylinders, heads, secs, translation;
HDGeometry geo;
if (guess_disk_lchs(blk, &cylinders, &heads, &secs) < 0) {
/* Try to probe the backing device geometry, otherwise fallback
to the old logic. (as of 12/2014 probing only succeeds on DASDs) */
if (blk_probe_geometry(blk, &geo) == 0) {
*pcyls = geo.cylinders;
*psecs = geo.sectors;
*pheads = geo.heads;
translation = BIOS_ATA_TRANSLATION_NONE;
} else if (guess_disk_lchs(blk, &cylinders, &heads, &secs) < 0) {
/* no LCHS guess: use a standard physical disk geometry */
guess_chs_for_size(blk, pcyls, pheads, psecs);
translation = hd_bios_chs_auto_trans(*pcyls, *pheads, *psecs);

View File

@ -765,6 +765,7 @@ static int nvme_init(PCIDevice *pci_dev)
if (!n->serial) {
return -1;
}
blkconf_blocksizes(&n->conf);
pci_conf = pci_dev->config;
pci_conf[PCI_INTERRUPT_PIN] = 1;

View File

@ -201,6 +201,7 @@ static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req)
#ifdef __linux__
int i;
VirtIOBlockIoctlReq *ioctl_req;
BlockAIOCB *acb;
#endif
/*
@ -278,8 +279,13 @@ static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req)
ioctl_req->hdr.sbp = elem->in_sg[elem->in_num - 3].iov_base;
ioctl_req->hdr.mx_sb_len = elem->in_sg[elem->in_num - 3].iov_len;
blk_aio_ioctl(blk->blk, SG_IO, &ioctl_req->hdr,
virtio_blk_ioctl_complete, ioctl_req);
acb = blk_aio_ioctl(blk->blk, SG_IO, &ioctl_req->hdr,
virtio_blk_ioctl_complete, ioctl_req);
if (!acb) {
g_free(ioctl_req);
status = VIRTIO_BLK_S_UNSUPP;
goto fail;
}
return -EINPROGRESS;
#else
abort();
@ -591,12 +597,6 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
if (mrb.num_reqs) {
virtio_blk_submit_multireq(s->blk, &mrb);
}
/*
* FIXME: Want to check for completions before returning to guest mode,
* so cached reads and writes are reported as quickly as possible. But
* that should be done in the generic block layer.
*/
}
static void virtio_blk_dma_restart_bh(void *opaque)
@ -884,6 +884,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
error_propagate(errp, err);
return;
}
blkconf_blocksizes(&conf->conf);
virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
sizeof(struct virtio_blk_config));

View File

@ -580,7 +580,8 @@ static void set_blocksize(Object *obj, Visitor *v, void *opaque,
error_propagate(errp, local_err);
return;
}
if (value < min || value > max) {
/* value of 0 means "unset" */
if (value && (value < min || value > max)) {
error_set(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE,
dev->id?:"", name, (int64_t)value, min, max);
return;

View File

@ -1160,6 +1160,11 @@ static void ahci_start_dma(IDEDMA *dma, IDEState *s,
dma_cb(s, 0);
}
static void ahci_restart_dma(IDEDMA *dma)
{
/* Nothing to do, ahci_start_dma already resets s->io_buffer_offset. */
}
/**
* Called in DMA R/W chains to read the PRDT, utilizing ahci_populate_sglist.
* Not currently invoked by PIO R/W chains,
@ -1226,12 +1231,6 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
return 1;
}
static int ahci_dma_set_unit(IDEDMA *dma, int unit)
{
/* only a single unit per link */
return 0;
}
static void ahci_cmd_done(IDEDMA *dma)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
@ -1252,19 +1251,14 @@ static void ahci_irq_set(void *opaque, int n, int level)
{
}
static void ahci_dma_restart_cb(void *opaque, int running, RunState state)
{
}
static const IDEDMAOps ahci_dma_ops = {
.start_dma = ahci_start_dma,
.restart_dma = ahci_restart_dma,
.start_transfer = ahci_start_transfer,
.prepare_buf = ahci_dma_prepare_buf,
.commit_buf = ahci_commit_buf,
.rw_buf = ahci_dma_rw_buf,
.set_unit = ahci_dma_set_unit,
.cmd_done = ahci_cmd_done,
.restart_cb = ahci_dma_restart_cb,
};
void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports)
@ -1294,6 +1288,7 @@ void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports)
ad->port_no = i;
ad->port.dma = &ad->dma;
ad->port.dma->ops = &ahci_dma_ops;
ide_register_restart_cb(&ad->port);
}
}
@ -1333,6 +1328,7 @@ static const VMStateDescription vmstate_ahci_device = {
.version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_IDE_BUS(port, AHCIDevice),
VMSTATE_IDE_DRIVE(port.ifs[0], AHCIDevice),
VMSTATE_UINT32(port_state, AHCIDevice),
VMSTATE_UINT32(finished, AHCIDevice),
VMSTATE_UINT32(port_regs.lst_addr, AHCIDevice),
@ -1371,16 +1367,23 @@ static int ahci_state_post_load(void *opaque, int version_id)
map_page(s->as, &ad->res_fis,
((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256);
/*
* All pending i/o should be flushed out on a migrate. However,
* we might not have cleared the busy_slot since this is done
* in a bh. Also, issue i/o against any slots that are pending.
* If an error is present, ad->busy_slot will be valid and not -1.
* In this case, an operation is waiting to resume and will re-check
* for additional AHCI commands to execute upon completion.
*
* In the case where no error was present, busy_slot will be -1,
* and we should check to see if there are additional commands waiting.
*/
if ((ad->busy_slot != -1) &&
!(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) {
pr->cmd_issue &= ~(1 << ad->busy_slot);
ad->busy_slot = -1;
if (ad->busy_slot == -1) {
check_cmd(s, i);
} else {
/* We are in the middle of a command, and may need to access
* the command header in guest memory again. */
if (ad->busy_slot < 0 || ad->busy_slot >= AHCI_MAX_CMDS) {
return -1;
}
ad->cur_cmd = &((AHCICmdHdr *)ad->lst)[ad->busy_slot];
}
check_cmd(s, i);
}
return 0;

View File

@ -252,7 +252,6 @@ static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size)
s->packet_transfer_size = size;
s->io_buffer_size = size; /* dma: send the reply data as one chunk */
s->elementary_transfer_size = 0;
s->io_buffer_index = 0;
if (s->atapi_dma) {
block_acct_start(blk_get_stats(s->blk), &s->acct, size,
@ -261,6 +260,7 @@ static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size)
ide_start_dma(s, ide_atapi_cmd_read_dma_cb);
} else {
s->status = READY_STAT | SEEK_STAT;
s->io_buffer_index = 0;
ide_atapi_cmd_reply_end(s);
}
}
@ -368,7 +368,6 @@ static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors,
{
s->lba = lba;
s->packet_transfer_size = nb_sectors * sector_size;
s->io_buffer_index = 0;
s->io_buffer_size = 0;
s->cd_sector_size = sector_size;

View File

@ -368,8 +368,7 @@ static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp)
bmdma_init(&d->bus[i], &d->bmdma[i], d);
d->bmdma[i].bus = &d->bus[i];
qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb,
&d->bmdma[i].dma);
ide_register_restart_cb(&d->bus[i]);
}
vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d);

View File

@ -561,6 +561,8 @@ static bool ide_sect_range_ok(IDEState *s,
return true;
}
static void ide_sector_read(IDEState *s);
static void ide_sector_read_cb(void *opaque, int ret)
{
IDEState *s = opaque;
@ -595,7 +597,7 @@ static void ide_sector_read_cb(void *opaque, int ret)
s->io_buffer_offset += 512 * n;
}
void ide_sector_read(IDEState *s)
static void ide_sector_read(IDEState *s)
{
int64_t sector_num;
int n;
@ -646,6 +648,9 @@ static void dma_buf_commit(IDEState *s, uint32_t tx_bytes)
void ide_set_inactive(IDEState *s, bool more)
{
s->bus->dma->aiocb = NULL;
s->bus->retry_unit = -1;
s->bus->retry_sector_num = 0;
s->bus->retry_nsector = 0;
if (s->bus->dma->ops->set_inactive) {
s->bus->dma->ops->set_inactive(s->bus->dma, more);
}
@ -666,7 +671,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
BlockErrorAction action = blk_get_error_action(s->blk, is_read, error);
if (action == BLOCK_ERROR_ACTION_STOP) {
s->bus->dma->ops->set_unit(s->bus->dma, s->unit);
assert(s->bus->retry_unit == s->unit);
s->bus->error_status = op;
} else if (action == BLOCK_ERROR_ACTION_REPORT) {
if (op & IDE_RETRY_DMA) {
@ -679,7 +684,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
return action != BLOCK_ERROR_ACTION_IGNORE;
}
void ide_dma_cb(void *opaque, int ret)
static void ide_dma_cb(void *opaque, int ret)
{
IDEState *s = opaque;
int n;
@ -777,7 +782,6 @@ eot:
static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd)
{
s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT;
s->io_buffer_index = 0;
s->io_buffer_size = 0;
s->dma_cmd = dma_cmd;
@ -799,11 +803,17 @@ static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd)
void ide_start_dma(IDEState *s, BlockCompletionFunc *cb)
{
s->io_buffer_index = 0;
s->bus->retry_unit = s->unit;
s->bus->retry_sector_num = ide_get_sector(s);
s->bus->retry_nsector = s->nsector;
if (s->bus->dma->ops->start_dma) {
s->bus->dma->ops->start_dma(s->bus->dma, s, cb);
}
}
static void ide_sector_write(IDEState *s);
static void ide_sector_write_timer_cb(void *opaque)
{
IDEState *s = opaque;
@ -863,7 +873,7 @@ static void ide_sector_write_cb(void *opaque, int ret)
}
}
void ide_sector_write(IDEState *s)
static void ide_sector_write(IDEState *s)
{
int64_t sector_num;
int n;
@ -917,7 +927,7 @@ static void ide_flush_cb(void *opaque, int ret)
ide_set_irq(s->bus);
}
void ide_flush_cache(IDEState *s)
static void ide_flush_cache(IDEState *s)
{
if (s->blk == NULL) {
ide_flush_cb(s, 0);
@ -2314,22 +2324,101 @@ static int ide_nop_int(IDEDMA *dma, int x)
return 0;
}
static void ide_nop(IDEDMA *dma)
{
}
static int32_t ide_nop_int32(IDEDMA *dma, int x)
{
return 0;
}
static void ide_nop_restart(void *opaque, int x, RunState y)
{
}
static const IDEDMAOps ide_dma_nop_ops = {
.prepare_buf = ide_nop_int32,
.restart_dma = ide_nop,
.rw_buf = ide_nop_int,
.set_unit = ide_nop_int,
.restart_cb = ide_nop_restart,
};
static void ide_restart_dma(IDEState *s, enum ide_dma_cmd dma_cmd)
{
s->unit = s->bus->retry_unit;
ide_set_sector(s, s->bus->retry_sector_num);
s->nsector = s->bus->retry_nsector;
s->bus->dma->ops->restart_dma(s->bus->dma);
s->io_buffer_size = 0;
s->dma_cmd = dma_cmd;
ide_start_dma(s, ide_dma_cb);
}
static void ide_restart_bh(void *opaque)
{
IDEBus *bus = opaque;
IDEState *s;
bool is_read;
int error_status;
qemu_bh_delete(bus->bh);
bus->bh = NULL;
error_status = bus->error_status;
if (bus->error_status == 0) {
return;
}
s = idebus_active_if(bus);
is_read = (bus->error_status & IDE_RETRY_READ) != 0;
/* The error status must be cleared before resubmitting the request: The
* request may fail again, and this case can only be distinguished if the
* called function can set a new error status. */
bus->error_status = 0;
if (error_status & IDE_RETRY_DMA) {
if (error_status & IDE_RETRY_TRIM) {
ide_restart_dma(s, IDE_DMA_TRIM);
} else {
ide_restart_dma(s, is_read ? IDE_DMA_READ : IDE_DMA_WRITE);
}
} else if (error_status & IDE_RETRY_PIO) {
if (is_read) {
ide_sector_read(s);
} else {
ide_sector_write(s);
}
} else if (error_status & IDE_RETRY_FLUSH) {
ide_flush_cache(s);
} else {
/*
* We've not got any bits to tell us about ATAPI - but
* we do have the end_transfer_func that tells us what
* we're trying to do.
*/
if (s->end_transfer_func == ide_atapi_cmd) {
ide_atapi_dma_restart(s);
}
}
}
static void ide_restart_cb(void *opaque, int running, RunState state)
{
IDEBus *bus = opaque;
if (!running)
return;
if (!bus->bh) {
bus->bh = qemu_bh_new(ide_restart_bh, bus);
qemu_bh_schedule(bus->bh);
}
}
void ide_register_restart_cb(IDEBus *bus)
{
if (bus->dma->ops->restart_dma) {
qemu_add_vm_change_state_handler(ide_restart_cb, bus);
}
}
static IDEDMA ide_dma_nop = {
.ops = &ide_dma_nop_ops,
.aiocb = NULL,
@ -2557,10 +2646,13 @@ const VMStateDescription vmstate_ide_drive = {
static const VMStateDescription vmstate_ide_error_status = {
.name ="ide_bus/error",
.version_id = 1,
.version_id = 2,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_INT32(error_status, IDEBus),
VMSTATE_INT64_V(retry_sector_num, IDEBus, 2),
VMSTATE_UINT32_V(retry_nsector, IDEBus, 2),
VMSTATE_UINT8_V(retry_unit, IDEBus, 2),
VMSTATE_END_OF_LIST()
}
};

View File

@ -436,10 +436,9 @@ struct IDEDMAOps {
DMAInt32Func *prepare_buf;
DMAu32Func *commit_buf;
DMAIntFunc *rw_buf;
DMAIntFunc *set_unit;
DMAVoidFunc *restart_dma;
DMAStopFunc *set_inactive;
DMAVoidFunc *cmd_done;
DMARestartFunc *restart_cb;
DMAVoidFunc *reset;
};
@ -455,6 +454,8 @@ struct IDEBus {
IDEDevice *master;
IDEDevice *slave;
IDEState ifs[2];
QEMUBH *bh;
int bus_id;
int max_units;
IDEDMA *dma;
@ -463,6 +464,9 @@ struct IDEBus {
qemu_irq irq;
int error_status;
uint8_t retry_unit;
int64_t retry_sector_num;
uint32_t retry_nsector;
};
#define TYPE_IDE_DEVICE "ide-device"
@ -522,6 +526,9 @@ extern const VMStateDescription vmstate_ide_drive;
#define VMSTATE_IDE_DRIVES(_field, _state) \
VMSTATE_STRUCT_ARRAY(_field, _state, 2, 3, vmstate_ide_drive, IDEState)
#define VMSTATE_IDE_DRIVE(_field, _state) \
VMSTATE_STRUCT(_field, _state, 1, vmstate_ide_drive, IDEState)
void ide_bus_reset(IDEBus *bus);
int64_t ide_get_sector(IDEState *s);
void ide_set_sector(IDEState *s, int64_t sector_num);
@ -550,12 +557,9 @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind,
int chs_trans);
void ide_init2(IDEBus *bus, qemu_irq irq);
void ide_init_ioport(IDEBus *bus, ISADevice *isa, int iobase, int iobase2);
void ide_register_restart_cb(IDEBus *bus);
void ide_exec_cmd(IDEBus *bus, uint32_t val);
void ide_dma_cb(void *opaque, int ret);
void ide_sector_write(IDEState *s);
void ide_sector_read(IDEState *s);
void ide_flush_cache(IDEState *s);
void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
EndTransferFunc *end_transfer_func);

View File

@ -74,7 +74,8 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp)
isa_init_irq(isadev, &s->irq, s->isairq);
ide_init2(&s->bus, s->irq);
vmstate_register(dev, 0, &vmstate_ide_isa, s);
};
ide_register_restart_cb(&s->bus);
}
ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq,
DriveInfo *hd0, DriveInfo *hd1)

View File

@ -558,10 +558,6 @@ static int32_t ide_nop_int32(IDEDMA *dma, int x)
return 0;
}
static void ide_nop_restart(void *opaque, int x, RunState y)
{
}
static void ide_dbdma_start(IDEDMA *dma, IDEState *s,
BlockCompletionFunc *cb)
{
@ -576,8 +572,6 @@ static const IDEDMAOps dbdma_ops = {
.start_dma = ide_dbdma_start,
.prepare_buf = ide_nop_int32,
.rw_buf = ide_nop_int,
.set_unit = ide_nop_int,
.restart_cb = ide_nop_restart,
};
static void macio_ide_realizefn(DeviceState *dev, Error **errp)

View File

@ -42,13 +42,10 @@ static void bmdma_start_dma(IDEDMA *dma, IDEState *s,
{
BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
bm->unit = s->unit;
bm->dma_cb = dma_cb;
bm->cur_prd_last = 0;
bm->cur_prd_addr = 0;
bm->cur_prd_len = 0;
bm->sector_num = ide_get_sector(s);
bm->nsector = s->nsector;
if (bm->status & BM_STATUS_DMAING) {
bm->dma_cb(bmdma_active_if(bm), 0);
@ -163,20 +160,11 @@ static int bmdma_rw_buf(IDEDMA *dma, int is_write)
return 1;
}
static int bmdma_set_unit(IDEDMA *dma, int unit)
{
BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
bm->unit = unit;
return 0;
}
static void bmdma_set_inactive(IDEDMA *dma, bool more)
{
BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
bm->dma_cb = NULL;
bm->unit = -1;
if (more) {
bm->status |= BM_STATUS_DMAING;
} else {
@ -184,83 +172,11 @@ static void bmdma_set_inactive(IDEDMA *dma, bool more)
}
}
static void bmdma_restart_dma(BMDMAState *bm, enum ide_dma_cmd dma_cmd)
static void bmdma_restart_dma(IDEDMA *dma)
{
IDEState *s = bmdma_active_if(bm);
ide_set_sector(s, bm->sector_num);
s->io_buffer_index = 0;
s->io_buffer_size = 0;
s->nsector = bm->nsector;
s->dma_cmd = dma_cmd;
bm->cur_addr = bm->addr;
bm->dma_cb = ide_dma_cb;
bmdma_start_dma(&bm->dma, s, bm->dma_cb);
}
/* TODO This should be common IDE code */
static void bmdma_restart_bh(void *opaque)
{
BMDMAState *bm = opaque;
IDEBus *bus = bm->bus;
bool is_read;
int error_status;
qemu_bh_delete(bm->bh);
bm->bh = NULL;
if (bm->unit == (uint8_t) -1) {
return;
}
is_read = (bus->error_status & IDE_RETRY_READ) != 0;
/* The error status must be cleared before resubmitting the request: The
* request may fail again, and this case can only be distinguished if the
* called function can set a new error status. */
error_status = bus->error_status;
bus->error_status = 0;
if (error_status & IDE_RETRY_DMA) {
if (error_status & IDE_RETRY_TRIM) {
bmdma_restart_dma(bm, IDE_DMA_TRIM);
} else {
bmdma_restart_dma(bm, is_read ? IDE_DMA_READ : IDE_DMA_WRITE);
}
} else if (error_status & IDE_RETRY_PIO) {
if (is_read) {
ide_sector_read(bmdma_active_if(bm));
} else {
ide_sector_write(bmdma_active_if(bm));
}
} else if (error_status & IDE_RETRY_FLUSH) {
ide_flush_cache(bmdma_active_if(bm));
} else {
IDEState *s = bmdma_active_if(bm);
/*
* We've not got any bits to tell us about ATAPI - but
* we do have the end_transfer_func that tells us what
* we're trying to do.
*/
if (s->end_transfer_func == ide_atapi_cmd) {
ide_atapi_dma_restart(s);
}
}
}
static void bmdma_restart_cb(void *opaque, int running, RunState state)
{
IDEDMA *dma = opaque;
BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
if (!running)
return;
if (!bm->bh) {
bm->bh = qemu_bh_new(bmdma_restart_bh, &bm->dma);
qemu_bh_schedule(bm->bh);
}
bm->cur_addr = bm->addr;
}
static void bmdma_cancel(BMDMAState *bm)
@ -286,8 +202,6 @@ static void bmdma_reset(IDEDMA *dma)
bm->cur_prd_last = 0;
bm->cur_prd_addr = 0;
bm->cur_prd_len = 0;
bm->sector_num = 0;
bm->nsector = 0;
}
static void bmdma_irq(void *opaque, int n, int level)
@ -404,6 +318,9 @@ static void ide_bmdma_pre_save(void *opaque)
BMDMAState *bm = opaque;
uint8_t abused_bits = BM_MIGRATION_COMPAT_STATUS_BITS;
bm->migration_retry_unit = bm->bus->retry_unit;
bm->migration_retry_sector_num = bm->bus->retry_sector_num;
bm->migration_retry_nsector = bm->bus->retry_nsector;
bm->migration_compat_status =
(bm->status & ~abused_bits) | (bm->bus->error_status & abused_bits);
}
@ -420,6 +337,11 @@ static int ide_bmdma_post_load(void *opaque, int version_id)
bm->status = bm->migration_compat_status & ~abused_bits;
bm->bus->error_status |= bm->migration_compat_status & abused_bits;
}
if (bm->bus->error_status) {
bm->bus->retry_sector_num = bm->migration_retry_sector_num;
bm->bus->retry_nsector = bm->migration_retry_nsector;
bm->bus->retry_unit = bm->migration_retry_unit;
}
return 0;
}
@ -456,9 +378,9 @@ static const VMStateDescription vmstate_bmdma = {
VMSTATE_UINT8(cmd, BMDMAState),
VMSTATE_UINT8(migration_compat_status, BMDMAState),
VMSTATE_UINT32(addr, BMDMAState),
VMSTATE_INT64(sector_num, BMDMAState),
VMSTATE_UINT32(nsector, BMDMAState),
VMSTATE_UINT8(unit, BMDMAState),
VMSTATE_INT64(migration_retry_sector_num, BMDMAState),
VMSTATE_UINT32(migration_retry_nsector, BMDMAState),
VMSTATE_UINT8(migration_retry_unit, BMDMAState),
VMSTATE_END_OF_LIST()
},
.subsections = (VMStateSubsection []) {
@ -482,7 +404,7 @@ static int ide_pci_post_load(void *opaque, int version_id)
for(i = 0; i < 2; i++) {
/* current versions always store 0/1, but older version
stored bigger values. We only need last bit */
d->bmdma[i].unit &= 1;
d->bmdma[i].migration_retry_unit &= 1;
ide_bmdma_post_load(&d->bmdma[i], -1);
}
@ -523,9 +445,8 @@ static const struct IDEDMAOps bmdma_ops = {
.start_dma = bmdma_start_dma,
.prepare_buf = bmdma_prepare_buf,
.rw_buf = bmdma_rw_buf,
.set_unit = bmdma_set_unit,
.restart_dma = bmdma_restart_dma,
.set_inactive = bmdma_set_inactive,
.restart_cb = bmdma_restart_cb,
.reset = bmdma_reset,
};

View File

@ -22,18 +22,18 @@ typedef struct BMDMAState {
uint32_t cur_prd_last;
uint32_t cur_prd_addr;
uint32_t cur_prd_len;
uint8_t unit;
BlockCompletionFunc *dma_cb;
int64_t sector_num;
uint32_t nsector;
MemoryRegion addr_ioport;
MemoryRegion extra_io;
QEMUBH *bh;
qemu_irq irq;
/* Bit 0-2 and 7: BM status register
* Bit 3-6: bus->error_status */
uint8_t migration_compat_status;
uint8_t migration_retry_unit;
int64_t migration_retry_sector_num;
uint32_t migration_retry_nsector;
struct PCIIDEState *pci_dev;
} BMDMAState;
@ -62,8 +62,8 @@ typedef struct PCIIDEState {
static inline IDEState *bmdma_active_if(BMDMAState *bmdma)
{
assert(bmdma->unit != (uint8_t)-1);
return bmdma->bus->ifs + bmdma->unit;
assert(bmdma->bus->retry_unit != (uint8_t)-1);
return bmdma->bus->ifs + bmdma->bus->retry_unit;
}

View File

@ -143,8 +143,7 @@ static void pci_piix_init_ports(PCIIDEState *d) {
bmdma_init(&d->bus[i], &d->bmdma[i], d);
d->bmdma[i].bus = &d->bus[i];
qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb,
&d->bmdma[i].dma);
ide_register_restart_cb(&d->bus[i]);
}
}

View File

@ -163,6 +163,7 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
return -1;
}
blkconf_blocksizes(&dev->conf);
if (dev->conf.logical_block_size != 512) {
error_report("logical_block_size must be 512 for IDE");
return -1;

View File

@ -166,8 +166,7 @@ static void vt82c686b_init_ports(PCIIDEState *d) {
bmdma_init(&d->bus[i], &d->bmdma[i], d);
d->bmdma[i].bus = &d->bus[i];
qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb,
&d->bmdma[i].dma);
ide_register_restart_cb(&d->bus[i]);
}
}

View File

@ -2251,6 +2251,7 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
}
blkconf_serial(&s->qdev.conf, &s->serial);
blkconf_blocksizes(&s->qdev.conf);
if (dev->type == TYPE_DISK) {
blkconf_geometry(&dev->conf, NULL, 65535, 255, 255, &err);
if (err) {
@ -2290,6 +2291,12 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
static void scsi_hd_realize(SCSIDevice *dev, Error **errp)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
/* can happen for devices without drive. The error message for missing
* backend will be issued in scsi_realize
*/
if (s->qdev.conf.blk) {
blkconf_blocksizes(&s->qdev.conf);
}
s->qdev.blocksize = s->qdev.conf.logical_block_size;
s->qdev.type = TYPE_DISK;
if (!s->product) {

View File

@ -611,6 +611,7 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
}
blkconf_serial(&s->conf, &dev->serial);
blkconf_blocksizes(&s->conf);
/*
* Hack alert: this pretends to be a block device, but it's really

View File

@ -60,6 +60,17 @@ typedef enum {
BDRV_REQ_MAY_UNMAP = 0x4,
} BdrvRequestFlags;
typedef struct BlockSizes {
uint32_t phys;
uint32_t log;
} BlockSizes;
typedef struct HDGeometry {
uint32_t heads;
uint32_t sectors;
uint32_t cylinders;
} HDGeometry;
#define BDRV_O_RDWR 0x0002
#define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */
#define BDRV_O_TEMPORARY 0x0010 /* delete the file after use */
@ -550,6 +561,8 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs);
* This function must be called with iothread lock held.
*/
void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context);
int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz);
int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo);
void bdrv_io_plug(BlockDriverState *bs);
void bdrv_io_unplug(BlockDriverState *bs);

View File

@ -56,6 +56,8 @@
#define BLOCK_OPT_ADAPTER_TYPE "adapter_type"
#define BLOCK_OPT_REDUNDANCY "redundancy"
#define BLOCK_OPT_NOCOW "nocow"
#define BLOCK_OPT_OBJECT_SIZE "object_size"
#define BLOCK_OPT_REFCOUNT_BITS "refcount_bits"
#define BLOCK_PROBE_BUF_SIZE 512
@ -273,6 +275,21 @@ struct BlockDriver {
void (*bdrv_io_unplug)(BlockDriverState *bs);
void (*bdrv_flush_io_queue)(BlockDriverState *bs);
/**
* Try to get @bs's logical and physical block size.
* On success, store them in @bsz and return zero.
* On failure, return negative errno.
*/
int (*bdrv_probe_blocksizes)(BlockDriverState *bs, BlockSizes *bsz);
/**
* Try to get @bs's geometry (cyls, heads, sectors)
* On success, store them in @geo and return 0.
* On failure return -errno.
* Only drivers that want to override guest geometry implement this
* callback; see hd_geometry_guess().
*/
int (*bdrv_probe_geometry)(BlockDriverState *bs, HDGeometry *geo);
QLIST_ENTRY(BlockDriver) list;
};

View File

@ -31,6 +31,7 @@
typedef enum {
COROUTINE_YIELD = 1,
COROUTINE_TERMINATE = 2,
COROUTINE_ENTER = 3,
} CoroutineAction;
struct Coroutine {

View File

@ -44,9 +44,9 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
#define DEFINE_BLOCK_PROPERTIES(_state, _conf) \
DEFINE_PROP_DRIVE("drive", _state, _conf.blk), \
DEFINE_PROP_BLOCKSIZE("logical_block_size", _state, \
_conf.logical_block_size, 512), \
_conf.logical_block_size), \
DEFINE_PROP_BLOCKSIZE("physical_block_size", _state, \
_conf.physical_block_size, 512), \
_conf.physical_block_size), \
DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0), \
DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \
DEFINE_PROP_UINT32("discard_granularity", _state, \
@ -63,6 +63,7 @@ void blkconf_serial(BlockConf *conf, char **serial);
void blkconf_geometry(BlockConf *conf, int *trans,
unsigned cyls_max, unsigned heads_max, unsigned secs_max,
Error **errp);
void blkconf_blocksizes(BlockConf *conf);
/* Hard disk geometry */

View File

@ -149,8 +149,8 @@ extern PropertyInfo qdev_prop_arraylen;
LostTickPolicy)
#define DEFINE_PROP_BIOS_CHS_TRANS(_n, _s, _f, _d) \
DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_bios_chs_trans, int)
#define DEFINE_PROP_BLOCKSIZE(_n, _s, _f, _d) \
DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_blocksize, uint16_t)
#define DEFINE_PROP_BLOCKSIZE(_n, _s, _f) \
DEFINE_PROP_DEFAULT(_n, _s, _f, 0, qdev_prop_blocksize, uint16_t)
#define DEFINE_PROP_PCI_HOST_DEVADDR(_n, _s, _f) \
DEFINE_PROP(_n, _s, _f, qdev_prop_pci_host_devaddr, PCIHostDeviceAddress)

View File

@ -164,5 +164,7 @@ int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors);
int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,
int64_t pos, int size);
int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size);
int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz);
int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo);
#endif

View File

@ -41,13 +41,16 @@
# @corrupt: #optional true if the image has been marked corrupt; only valid for
# compat >= 1.1 (since 2.2)
#
# @refcount-bits: width of a refcount entry in bits (since 2.3)
#
# Since: 1.7
##
{ 'type': 'ImageInfoSpecificQCow2',
'data': {
'compat': 'str',
'*lazy-refcounts': 'bool',
'*corrupt': 'bool'
'*corrupt': 'bool',
'refcount-bits': 'int'
} }
##

View File

@ -99,29 +99,10 @@ static void coroutine_delete(Coroutine *co)
qemu_coroutine_delete(co);
}
static void coroutine_swap(Coroutine *from, Coroutine *to)
{
CoroutineAction ret;
ret = qemu_coroutine_switch(from, to, COROUTINE_YIELD);
qemu_co_queue_run_restart(to);
switch (ret) {
case COROUTINE_YIELD:
return;
case COROUTINE_TERMINATE:
trace_qemu_coroutine_terminate(to);
coroutine_delete(to);
return;
default:
abort();
}
}
void qemu_coroutine_enter(Coroutine *co, void *opaque)
{
Coroutine *self = qemu_coroutine_self();
CoroutineAction ret;
trace_qemu_coroutine_enter(self, co, opaque);
@ -132,7 +113,20 @@ void qemu_coroutine_enter(Coroutine *co, void *opaque)
co->caller = self;
co->entry_arg = opaque;
coroutine_swap(self, co);
ret = qemu_coroutine_switch(self, co, COROUTINE_ENTER);
qemu_co_queue_run_restart(co);
switch (ret) {
case COROUTINE_YIELD:
return;
case COROUTINE_TERMINATE:
trace_qemu_coroutine_terminate(co);
coroutine_delete(co);
return;
default:
abort();
}
}
void coroutine_fn qemu_coroutine_yield(void)
@ -148,5 +142,5 @@ void coroutine_fn qemu_coroutine_yield(void)
}
self->caller = NULL;
coroutine_swap(self, to);
qemu_coroutine_switch(self, to, COROUTINE_YIELD);
}

View File

@ -191,6 +191,8 @@ gcov-files-sparc-y += hw/timer/m48t59.c
gcov-files-sparc64-y += hw/timer/m48t59.c
check-qtest-arm-y = tests/tmp105-test$(EXESUF)
gcov-files-arm-y += hw/misc/tmp105.c
check-qtest-arm-y += tests/virtio-blk-test$(EXESUF)
gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c
check-qtest-ppc-y += tests/boot-order-test$(EXESUF)
check-qtest-ppc64-y += tests/boot-order-test$(EXESUF)
check-qtest-ppc64-y += tests/spapr-phb-test$(EXESUF)
@ -315,8 +317,8 @@ libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o
libqos-pc-obj-y += tests/libqos/malloc-pc.o tests/libqos/libqos-pc.o
libqos-pc-obj-y += tests/libqos/ahci.o
libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o
libqos-virtio-obj-y = $(libqos-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o
libqos-usb-obj-y = $(libqos-pc-obj-y) tests/libqos/usb.o
libqos-virtio-obj-y = $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o
tests/rtc-test$(EXESUF): tests/rtc-test.o
tests/m48t59-test$(EXESUF): tests/m48t59-test.o

View File

@ -107,6 +107,21 @@ static void ahci_shutdown(AHCIQState *ahci)
qtest_shutdown(qs);
}
/**
* Boot and fully enable the HBA device.
* @see ahci_boot, ahci_pci_enable and ahci_hba_enable.
*/
static AHCIQState *ahci_boot_and_enable(void)
{
AHCIQState *ahci;
ahci = ahci_boot();
ahci_pci_enable(ahci);
ahci_hba_enable(ahci);
return ahci;
}
/*** Specification Adherence Tests ***/
/**
@ -716,12 +731,12 @@ static void ahci_test_identify(AHCIQState *ahci)
g_assert_cmphex(sect_size, ==, 0x200);
}
static void ahci_test_dma_rw_simple(AHCIQState *ahci)
static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize,
uint8_t read_cmd, uint8_t write_cmd)
{
uint64_t ptr;
uint8_t port;
unsigned i;
const unsigned bufsize = 4096;
unsigned char *tx = g_malloc(bufsize);
unsigned char *rx = g_malloc0(bufsize);
@ -736,16 +751,16 @@ static void ahci_test_dma_rw_simple(AHCIQState *ahci)
ptr = ahci_alloc(ahci, bufsize);
g_assert(ptr);
/* Write some indicative pattern to our 4K buffer. */
/* Write some indicative pattern to our buffer. */
for (i = 0; i < bufsize; i++) {
tx[i] = (bufsize - i);
}
memwrite(ptr, tx, bufsize);
/* Write this buffer to disk, then read it back to the DMA buffer. */
ahci_guest_io(ahci, port, CMD_WRITE_DMA, ptr, bufsize);
ahci_guest_io(ahci, port, write_cmd, ptr, bufsize);
qmemset(ptr, 0x00, bufsize);
ahci_guest_io(ahci, port, CMD_READ_DMA, ptr, bufsize);
ahci_guest_io(ahci, port, read_cmd, ptr, bufsize);
/*** Read back the Data ***/
memread(ptr, rx, bufsize);
@ -831,27 +846,206 @@ static void test_identify(void)
{
AHCIQState *ahci;
ahci = ahci_boot();
ahci_pci_enable(ahci);
ahci_hba_enable(ahci);
ahci = ahci_boot_and_enable();
ahci_test_identify(ahci);
ahci_shutdown(ahci);
}
/**
* Perform a simple DMA R/W test, using a single PRD and non-NCQ commands.
* Fragmented DMA test: Perform a standard 4K DMA read/write
* test, but make sure the physical regions are fragmented to
* be very small, each just 32 bytes, to see how AHCI performs
* with chunks defined to be much less than a sector.
*/
static void test_dma_rw_simple(void)
static void test_dma_fragmented(void)
{
AHCIQState *ahci;
AHCICommand *cmd;
uint8_t px;
size_t bufsize = 4096;
unsigned char *tx = g_malloc(bufsize);
unsigned char *rx = g_malloc0(bufsize);
unsigned i;
uint64_t ptr;
ahci = ahci_boot_and_enable();
px = ahci_port_select(ahci);
ahci_port_clear(ahci, px);
/* create pattern */
for (i = 0; i < bufsize; i++) {
tx[i] = (bufsize - i);
}
/* Create a DMA buffer in guest memory, and write our pattern to it. */
ptr = guest_alloc(ahci->parent->alloc, bufsize);
g_assert(ptr);
memwrite(ptr, tx, bufsize);
cmd = ahci_command_create(CMD_WRITE_DMA);
ahci_command_adjust(cmd, 0, ptr, bufsize, 32);
ahci_command_commit(ahci, cmd, px);
ahci_command_issue(ahci, cmd);
ahci_command_verify(ahci, cmd);
g_free(cmd);
cmd = ahci_command_create(CMD_READ_DMA);
ahci_command_adjust(cmd, 0, ptr, bufsize, 32);
ahci_command_commit(ahci, cmd, px);
ahci_command_issue(ahci, cmd);
ahci_command_verify(ahci, cmd);
g_free(cmd);
/* Read back the guest's receive buffer into local memory */
memread(ptr, rx, bufsize);
guest_free(ahci->parent->alloc, ptr);
g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0);
ahci_shutdown(ahci);
g_free(rx);
g_free(tx);
}
/******************************************************************************/
/* AHCI I/O Test Matrix Definitions */
enum BuffLen {
LEN_BEGIN = 0,
LEN_SIMPLE = LEN_BEGIN,
LEN_DOUBLE,
LEN_LONG,
LEN_SHORT,
NUM_LENGTHS
};
static const char *buff_len_str[NUM_LENGTHS] = { "simple", "double",
"long", "short" };
enum AddrMode {
ADDR_MODE_BEGIN = 0,
ADDR_MODE_LBA28 = ADDR_MODE_BEGIN,
ADDR_MODE_LBA48,
NUM_ADDR_MODES
};
static const char *addr_mode_str[NUM_ADDR_MODES] = { "lba28", "lba48" };
enum IOMode {
MODE_BEGIN = 0,
MODE_PIO = MODE_BEGIN,
MODE_DMA,
NUM_MODES
};
static const char *io_mode_str[NUM_MODES] = { "pio", "dma" };
enum IOOps {
IO_BEGIN = 0,
IO_READ = IO_BEGIN,
IO_WRITE,
NUM_IO_OPS
};
typedef struct AHCIIOTestOptions {
enum BuffLen length;
enum AddrMode address_type;
enum IOMode io_type;
} AHCIIOTestOptions;
/**
* Table of possible I/O ATA commands given a set of enumerations.
*/
static const uint8_t io_cmds[NUM_MODES][NUM_ADDR_MODES][NUM_IO_OPS] = {
[MODE_PIO] = {
[ADDR_MODE_LBA28] = {
[IO_READ] = CMD_READ_PIO,
[IO_WRITE] = CMD_WRITE_PIO },
[ADDR_MODE_LBA48] = {
[IO_READ] = CMD_READ_PIO_EXT,
[IO_WRITE] = CMD_WRITE_PIO_EXT }
},
[MODE_DMA] = {
[ADDR_MODE_LBA28] = {
[IO_READ] = CMD_READ_DMA,
[IO_WRITE] = CMD_WRITE_DMA },
[ADDR_MODE_LBA48] = {
[IO_READ] = CMD_READ_DMA_EXT,
[IO_WRITE] = CMD_WRITE_DMA_EXT }
}
};
/**
* Test a Read/Write pattern using various commands, addressing modes,
* transfer modes, and buffer sizes.
*/
static void test_io_rw_interface(enum AddrMode lba48, enum IOMode dma,
unsigned bufsize)
{
AHCIQState *ahci;
ahci = ahci_boot();
ahci_pci_enable(ahci);
ahci_hba_enable(ahci);
ahci_test_dma_rw_simple(ahci);
ahci = ahci_boot_and_enable();
ahci_test_io_rw_simple(ahci, bufsize,
io_cmds[dma][lba48][IO_READ],
io_cmds[dma][lba48][IO_WRITE]);
ahci_shutdown(ahci);
}
/**
* Demultiplex the test data and invoke the actual test routine.
*/
static void test_io_interface(gconstpointer opaque)
{
AHCIIOTestOptions *opts = (AHCIIOTestOptions *)opaque;
unsigned bufsize;
switch (opts->length) {
case LEN_SIMPLE:
bufsize = 4096;
break;
case LEN_DOUBLE:
bufsize = 8192;
break;
case LEN_LONG:
bufsize = 4096 * 64;
break;
case LEN_SHORT:
bufsize = 512;
break;
default:
g_assert_not_reached();
}
test_io_rw_interface(opts->address_type, opts->io_type, bufsize);
g_free(opts);
return;
}
static void create_ahci_io_test(enum IOMode type, enum AddrMode addr,
enum BuffLen len)
{
static const char *arch;
char *name;
AHCIIOTestOptions *opts = g_malloc(sizeof(AHCIIOTestOptions));
opts->length = len;
opts->address_type = addr;
opts->io_type = type;
if (!arch) {
arch = qtest_get_arch();
}
name = g_strdup_printf("/%s/ahci/io/%s/%s/%s", arch,
io_mode_str[type],
addr_mode_str[addr],
buff_len_str[len]);
g_test_add_data_func(name, opts, test_io_interface);
g_free(name);
}
/******************************************************************************/
int main(int argc, char **argv)
@ -860,6 +1054,7 @@ int main(int argc, char **argv)
int fd;
int ret;
int c;
int i, j, k;
static struct option long_options[] = {
{"pedantic", no_argument, 0, 'p' },
@ -907,7 +1102,16 @@ int main(int argc, char **argv)
qtest_add_func("/ahci/hba_spec", test_hba_spec);
qtest_add_func("/ahci/hba_enable", test_hba_enable);
qtest_add_func("/ahci/identify", test_identify);
qtest_add_func("/ahci/dma/simple", test_dma_rw_simple);
for (i = MODE_BEGIN; i < NUM_MODES; i++) {
for (j = ADDR_MODE_BEGIN; j < NUM_ADDR_MODES; j++) {
for (k = LEN_BEGIN; k < NUM_LENGTHS; k++) {
create_ahci_io_test(i, j, k);
}
}
}
qtest_add_func("/ahci/io/dma/lba28/fragmented", test_dma_fragmented);
ret = g_test_run();

View File

@ -118,7 +118,6 @@ static void ide_test_start(const char *cmdline_fmt, ...)
va_end(ap);
qtest_start(cmdline);
qtest_irq_intercept_in(global_qtest, "ioapic");
guest_malloc = pc_alloc_init();
g_free(cmdline);
@ -388,6 +387,7 @@ static void test_bmdma_setup(void)
"-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw "
"-global ide-hd.ver=%s",
tmp_path, "testdisk", "version");
qtest_irq_intercept_in(global_qtest, "ioapic");
}
static void test_bmdma_teardown(void)
@ -516,7 +516,7 @@ static void prepare_blkdebug_script(const char *debug_fn, const char *event)
g_assert(ret == 0);
}
static void test_retry_flush(void)
static void test_retry_flush(const char *machine)
{
uint8_t data;
const char *s;
@ -580,6 +580,16 @@ static void test_flush_nodev(void)
ide_test_quit();
}
static void test_pci_retry_flush(const char *machine)
{
test_retry_flush("pc");
}
static void test_isa_retry_flush(const char *machine)
{
test_retry_flush("isapc");
}
int main(int argc, char **argv)
{
const char *arch = qtest_get_arch();
@ -617,9 +627,9 @@ int main(int argc, char **argv)
qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown);
qtest_add_func("/ide/flush", test_flush);
qtest_add_func("/ide/flush_nodev", test_flush_nodev);
qtest_add_func("/ide/retry/flush", test_retry_flush);
qtest_add_func("/ide/flush/nodev", test_flush_nodev);
qtest_add_func("/ide/flush/retry_pci", test_pci_retry_flush);
qtest_add_func("/ide/flush/retry_isa", test_isa_retry_flush);
ret = g_test_run();

View File

@ -487,7 +487,7 @@ void ahci_get_command_header(AHCIQState *ahci, uint8_t port,
void ahci_set_command_header(AHCIQState *ahci, uint8_t port,
uint8_t slot, AHCICommandHeader *cmd)
{
AHCICommandHeader tmp;
AHCICommandHeader tmp = { .flags = 0 };
uint64_t ba = ahci->port[port].clb;
ba += slot * sizeof(AHCICommandHeader);
@ -713,6 +713,40 @@ void ahci_command_free(AHCICommand *cmd)
g_free(cmd);
}
void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags)
{
cmd->header.flags |= cmdh_flags;
}
void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags)
{
cmd->header.flags &= ~cmdh_flags;
}
void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect)
{
RegH2DFIS *fis = &(cmd->fis);
if (cmd->props->lba28) {
g_assert_cmphex(lba_sect, <=, 0xFFFFFFF);
} else if (cmd->props->lba48) {
g_assert_cmphex(lba_sect, <=, 0xFFFFFFFFFFFF);
} else {
/* Can't set offset if we don't know the format. */
g_assert_not_reached();
}
/* LBA28 uses the low nibble of the device/control register for LBA24:27 */
fis->lba_lo[0] = (lba_sect & 0xFF);
fis->lba_lo[1] = (lba_sect >> 8) & 0xFF;
fis->lba_lo[2] = (lba_sect >> 16) & 0xFF;
if (cmd->props->lba28) {
fis->device = (fis->device & 0xF0) || (lba_sect >> 24) & 0x0F;
}
fis->lba_hi[0] = (lba_sect >> 24) & 0xFF;
fis->lba_hi[1] = (lba_sect >> 32) & 0xFF;
fis->lba_hi[2] = (lba_sect >> 40) & 0xFF;
}
void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer)
{
cmd->buffer = buffer;
@ -740,6 +774,14 @@ void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size)
ahci_command_set_sizes(cmd, cmd->xbytes, prd_size);
}
void ahci_command_adjust(AHCICommand *cmd, uint64_t offset, uint64_t buffer,
uint64_t xbytes, unsigned prd_size)
{
ahci_command_set_sizes(cmd, xbytes, prd_size);
ahci_command_set_buffer(cmd, buffer);
ahci_command_set_offset(cmd, offset);
}
void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port)
{
uint16_t i, prdtl;

View File

@ -537,11 +537,16 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd);
void ahci_command_free(AHCICommand *cmd);
/* Command adjustments */
void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags);
void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags);
void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect);
void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer);
void ahci_command_set_size(AHCICommand *cmd, uint64_t xbytes);
void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size);
void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
unsigned prd_size);
void ahci_command_adjust(AHCICommand *cmd, uint64_t lba_sect, uint64_t gbuffer,
uint64_t xbytes, unsigned prd_size);
/* Command Misc */
uint8_t ahci_command_slot(AHCICommand *cmd);

View File

@ -0,0 +1,39 @@
/*
* Basic libqos generic malloc support
*
* Copyright (c) 2014 Marc Marí
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include <glib.h>
#include "libqos/malloc-generic.h"
#include "libqos/malloc.h"
/*
* Mostly for valgrind happiness, but it does offer
* a chokepoint for debugging guest memory leaks, too.
*/
void generic_alloc_uninit(QGuestAllocator *allocator)
{
alloc_uninit(allocator);
}
QGuestAllocator *generic_alloc_init_flags(uint64_t base_addr, uint64_t size,
uint32_t page_size, QAllocOpts flags)
{
QGuestAllocator *s;
uint64_t start = base_addr + (1 << 20); /* Start at 1MB */
s = alloc_init_flags(flags, start, start + size);
alloc_set_page_size(s, page_size);
return s;
}
inline QGuestAllocator *generic_alloc_init(uint64_t base_addr, uint64_t size,
uint32_t page_size)
{
return generic_alloc_init_flags(base_addr, size, page_size, ALLOC_NO_FLAGS);
}

View File

@ -0,0 +1,21 @@
/*
* Basic libqos generic malloc support
*
* Copyright (c) 2014 Marc Marí
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef LIBQOS_MALLOC_GENERIC_H
#define LIBQOS_MALLOC_GENERIC_H
#include "libqos/malloc.h"
QGuestAllocator *generic_alloc_init(uint64_t base_addr, uint64_t size,
uint32_t page_size);
QGuestAllocator *generic_alloc_init_flags(uint64_t base_addr, uint64_t size,
uint32_t page_size, QAllocOpts flags);
void generic_alloc_uninit(QGuestAllocator *allocator);
#endif

198
tests/libqos/virtio-mmio.c Normal file
View File

@ -0,0 +1,198 @@
/*
* libqos virtio MMIO driver
*
* Copyright (c) 2014 Marc Marí
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include <glib.h>
#include <stdio.h>
#include "libqtest.h"
#include "libqos/virtio.h"
#include "libqos/virtio-mmio.h"
#include "libqos/malloc.h"
#include "libqos/malloc-generic.h"
static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t addr)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
return readb(dev->addr + addr);
}
static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t addr)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
return readw(dev->addr + addr);
}
static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t addr)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
return readl(dev->addr + addr);
}
static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t addr)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
return readq(dev->addr + addr);
}
static uint32_t qvirtio_mmio_get_features(QVirtioDevice *d)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
writel(dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0);
return readl(dev->addr + QVIRTIO_MMIO_HOST_FEATURES);
}
static void qvirtio_mmio_set_features(QVirtioDevice *d, uint32_t features)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
dev->features = features;
writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0);
writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features);
}
static uint32_t qvirtio_mmio_get_guest_features(QVirtioDevice *d)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
return dev->features;
}
static uint8_t qvirtio_mmio_get_status(QVirtioDevice *d)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
return (uint8_t)readl(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS);
}
static void qvirtio_mmio_set_status(QVirtioDevice *d, uint8_t status)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
writel(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS, (uint32_t)status);
}
static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
uint32_t isr;
isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 1;
if (isr != 0) {
writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 1);
return true;
}
return false;
}
static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice *d)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
uint32_t isr;
isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 2;
if (isr != 0) {
writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 2);
return true;
}
return false;
}
static void qvirtio_mmio_queue_select(QVirtioDevice *d, uint16_t index)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
writel(dev->addr + QVIRTIO_MMIO_QUEUE_SEL, (uint32_t)index);
g_assert_cmphex(readl(dev->addr + QVIRTIO_MMIO_QUEUE_PFN), ==, 0);
}
static uint16_t qvirtio_mmio_get_queue_size(QVirtioDevice *d)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
return (uint16_t)readl(dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX);
}
static void qvirtio_mmio_set_queue_address(QVirtioDevice *d, uint32_t pfn)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
writel(dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn);
}
static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d,
QGuestAllocator *alloc, uint16_t index)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
QVirtQueue *vq;
uint64_t addr;
vq = g_malloc0(sizeof(*vq));
qvirtio_mmio_queue_select(d, index);
writel(dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size);
vq->index = index;
vq->size = qvirtio_mmio_get_queue_size(d);
vq->free_head = 0;
vq->num_free = vq->size;
vq->align = dev->page_size;
vq->indirect = (dev->features & QVIRTIO_F_RING_INDIRECT_DESC) != 0;
vq->event = (dev->features & QVIRTIO_F_RING_EVENT_IDX) != 0;
writel(dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size);
/* Check different than 0 */
g_assert_cmpint(vq->size, !=, 0);
/* Check power of 2 */
g_assert_cmpint(vq->size & (vq->size - 1), ==, 0);
addr = guest_alloc(alloc, qvring_size(vq->size, dev->page_size));
qvring_init(alloc, vq, addr);
qvirtio_mmio_set_queue_address(d, vq->desc / dev->page_size);
return vq;
}
static void qvirtio_mmio_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
writel(dev->addr + QVIRTIO_MMIO_QUEUE_NOTIFY, vq->index);
}
const QVirtioBus qvirtio_mmio = {
.config_readb = qvirtio_mmio_config_readb,
.config_readw = qvirtio_mmio_config_readw,
.config_readl = qvirtio_mmio_config_readl,
.config_readq = qvirtio_mmio_config_readq,
.get_features = qvirtio_mmio_get_features,
.set_features = qvirtio_mmio_set_features,
.get_guest_features = qvirtio_mmio_get_guest_features,
.get_status = qvirtio_mmio_get_status,
.set_status = qvirtio_mmio_set_status,
.get_queue_isr_status = qvirtio_mmio_get_queue_isr_status,
.get_config_isr_status = qvirtio_mmio_get_config_isr_status,
.queue_select = qvirtio_mmio_queue_select,
.get_queue_size = qvirtio_mmio_get_queue_size,
.set_queue_address = qvirtio_mmio_set_queue_address,
.virtqueue_setup = qvirtio_mmio_virtqueue_setup,
.virtqueue_kick = qvirtio_mmio_virtqueue_kick,
};
QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size)
{
QVirtioMMIODevice *dev;
uint32_t magic;
dev = g_malloc0(sizeof(*dev));
magic = readl(addr + QVIRTIO_MMIO_MAGIC_VALUE);
g_assert(magic == ('v' | 'i' << 8 | 'r' << 16 | 't' << 24));
dev->addr = addr;
dev->page_size = page_size;
dev->vdev.device_type = readl(addr + QVIRTIO_MMIO_DEVICE_ID);
writel(addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size);
return dev;
}

View File

@ -0,0 +1,46 @@
/*
* libqos virtio MMIO definitions
*
* Copyright (c) 2014 Marc Marí
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef LIBQOS_VIRTIO_MMIO_H
#define LIBQOS_VIRTIO_MMIO_H
#include "libqos/virtio.h"
#define QVIRTIO_MMIO_MAGIC_VALUE 0x000
#define QVIRTIO_MMIO_VERSION 0x004
#define QVIRTIO_MMIO_DEVICE_ID 0x008
#define QVIRTIO_MMIO_VENDOR_ID 0x00C
#define QVIRTIO_MMIO_HOST_FEATURES 0x010
#define QVIRTIO_MMIO_HOST_FEATURES_SEL 0x014
#define QVIRTIO_MMIO_GUEST_FEATURES 0x020
#define QVIRTIO_MMIO_GUEST_FEATURES_SEL 0x024
#define QVIRTIO_MMIO_GUEST_PAGE_SIZE 0x028
#define QVIRTIO_MMIO_QUEUE_SEL 0x030
#define QVIRTIO_MMIO_QUEUE_NUM_MAX 0x034
#define QVIRTIO_MMIO_QUEUE_NUM 0x038
#define QVIRTIO_MMIO_QUEUE_ALIGN 0x03C
#define QVIRTIO_MMIO_QUEUE_PFN 0x040
#define QVIRTIO_MMIO_QUEUE_NOTIFY 0x050
#define QVIRTIO_MMIO_INTERRUPT_STATUS 0x060
#define QVIRTIO_MMIO_INTERRUPT_ACK 0x064
#define QVIRTIO_MMIO_DEVICE_STATUS 0x070
#define QVIRTIO_MMIO_DEVICE_SPECIFIC 0x100
typedef struct QVirtioMMIODevice {
QVirtioDevice vdev;
uint64_t addr;
uint32_t page_size;
uint32_t features; /* As it cannot be read later, save it */
} QVirtioMMIODevice;
extern const QVirtioBus qvirtio_mmio;
QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size);
#endif

View File

@ -60,25 +60,25 @@ static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data)
*vpcidev = (QVirtioPCIDevice *)d;
}
static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, void *addr)
static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t addr)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
return qpci_io_readb(dev->pdev, addr);
return qpci_io_readb(dev->pdev, (void *)(uintptr_t)addr);
}
static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, void *addr)
static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t addr)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
return qpci_io_readw(dev->pdev, addr);
return qpci_io_readw(dev->pdev, (void *)(uintptr_t)addr);
}
static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, void *addr)
static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t addr)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
return qpci_io_readl(dev->pdev, addr);
return qpci_io_readl(dev->pdev, (void *)(uintptr_t)addr);
}
static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, void *addr)
static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t addr)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
int i;
@ -86,11 +86,13 @@ static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, void *addr)
if (qtest_big_endian()) {
for (i = 0; i < 8; ++i) {
u64 |= (uint64_t)qpci_io_readb(dev->pdev, addr + i) << (7 - i) * 8;
u64 |= (uint64_t)qpci_io_readb(dev->pdev,
(void *)(uintptr_t)addr + i) << (7 - i) * 8;
}
} else {
for (i = 0; i < 8; ++i) {
u64 |= (uint64_t)qpci_io_readb(dev->pdev, addr + i) << i * 8;
u64 |= (uint64_t)qpci_io_readb(dev->pdev,
(void *)(uintptr_t)addr + i) << i * 8;
}
}
@ -100,31 +102,31 @@ static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, void *addr)
static uint32_t qvirtio_pci_get_features(QVirtioDevice *d)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_DEVICE_FEATURES);
return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_FEATURES);
}
static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_GUEST_FEATURES, features);
qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_PCI_GUEST_FEATURES, features);
}
static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_GUEST_FEATURES);
return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_PCI_GUEST_FEATURES);
}
static uint8_t qvirtio_pci_get_status(QVirtioDevice *d)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_DEVICE_STATUS);
return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_STATUS);
}
static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_DEVICE_STATUS, status);
qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_STATUS, status);
}
static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
@ -140,11 +142,15 @@ static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
return qpci_msix_pending(dev->pdev, vqpci->msix_entry);
} else {
data = readl(vqpci->msix_addr);
writel(vqpci->msix_addr, 0);
return data == vqpci->msix_data;
if (data == vqpci->msix_data) {
writel(vqpci->msix_addr, 0);
return true;
} else {
return false;
}
}
} else {
return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_ISR_STATUS) & 1;
return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_ISR_STATUS) & 1;
}
}
@ -160,30 +166,34 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d)
return qpci_msix_pending(dev->pdev, dev->config_msix_entry);
} else {
data = readl(dev->config_msix_addr);
writel(dev->config_msix_addr, 0);
return data == dev->config_msix_data;
if (data == dev->config_msix_data) {
writel(dev->config_msix_addr, 0);
return true;
} else {
return false;
}
}
} else {
return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_ISR_STATUS) & 2;
return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_ISR_STATUS) & 2;
}
}
static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_QUEUE_SELECT, index);
qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_SELECT, index);
}
static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
return qpci_io_readw(dev->pdev, dev->addr + QVIRTIO_QUEUE_SIZE);
return qpci_io_readw(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_SIZE);
}
static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_QUEUE_ADDRESS, pfn);
qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_ADDRESS, pfn);
}
static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d,
@ -225,7 +235,7 @@ static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d,
static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
qpci_io_writew(dev->pdev, dev->addr + QVIRTIO_QUEUE_NOTIFY, vq->index);
qpci_io_writew(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_NOTIFY, vq->index);
}
const QVirtioBus qvirtio_pci = {
@ -305,8 +315,8 @@ void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci,
control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index);
qpci_io_writew(d->pdev, d->addr + QVIRTIO_MSIX_QUEUE_VECTOR, entry);
vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_MSIX_QUEUE_VECTOR);
qpci_io_writew(d->pdev, d->addr + QVIRTIO_PCI_MSIX_QUEUE_VECTOR, entry);
vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_PCI_MSIX_QUEUE_VECTOR);
g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR);
}
@ -337,7 +347,7 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d,
qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL,
control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
qpci_io_writew(d->pdev, d->addr + QVIRTIO_MSIX_CONF_VECTOR, entry);
vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_MSIX_CONF_VECTOR);
qpci_io_writew(d->pdev, d->addr + QVIRTIO_PCI_MSIX_CONF_VECTOR, entry);
vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_PCI_MSIX_CONF_VECTOR);
g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR);
}

View File

@ -13,18 +13,18 @@
#include "libqos/virtio.h"
#include "libqos/pci.h"
#define QVIRTIO_DEVICE_FEATURES 0x00
#define QVIRTIO_GUEST_FEATURES 0x04
#define QVIRTIO_QUEUE_ADDRESS 0x08
#define QVIRTIO_QUEUE_SIZE 0x0C
#define QVIRTIO_QUEUE_SELECT 0x0E
#define QVIRTIO_QUEUE_NOTIFY 0x10
#define QVIRTIO_DEVICE_STATUS 0x12
#define QVIRTIO_ISR_STATUS 0x13
#define QVIRTIO_MSIX_CONF_VECTOR 0x14
#define QVIRTIO_MSIX_QUEUE_VECTOR 0x16
#define QVIRTIO_DEVICE_SPECIFIC_MSIX 0x18
#define QVIRTIO_DEVICE_SPECIFIC_NO_MSIX 0x14
#define QVIRTIO_PCI_DEVICE_FEATURES 0x00
#define QVIRTIO_PCI_GUEST_FEATURES 0x04
#define QVIRTIO_PCI_QUEUE_ADDRESS 0x08
#define QVIRTIO_PCI_QUEUE_SIZE 0x0C
#define QVIRTIO_PCI_QUEUE_SELECT 0x0E
#define QVIRTIO_PCI_QUEUE_NOTIFY 0x10
#define QVIRTIO_PCI_DEVICE_STATUS 0x12
#define QVIRTIO_PCI_ISR_STATUS 0x13
#define QVIRTIO_PCI_MSIX_CONF_VECTOR 0x14
#define QVIRTIO_PCI_MSIX_QUEUE_VECTOR 0x16
#define QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX 0x18
#define QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX 0x14
#define QVIRTIO_PCI_ALIGN 4096

View File

@ -12,25 +12,25 @@
#include "libqos/virtio.h"
uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d,
void *addr)
uint64_t addr)
{
return bus->config_readb(d, addr);
}
uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d,
void *addr)
uint64_t addr)
{
return bus->config_readw(d, addr);
}
uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d,
void *addr)
uint64_t addr)
{
return bus->config_readl(d, addr);
}
uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d,
void *addr)
uint64_t addr)
{
return bus->config_readq(d, addr);
}

View File

@ -93,10 +93,10 @@ typedef struct QVRingIndirectDesc {
} QVRingIndirectDesc;
typedef struct QVirtioBus {
uint8_t (*config_readb)(QVirtioDevice *d, void *addr);
uint16_t (*config_readw)(QVirtioDevice *d, void *addr);
uint32_t (*config_readl)(QVirtioDevice *d, void *addr);
uint64_t (*config_readq)(QVirtioDevice *d, void *addr);
uint8_t (*config_readb)(QVirtioDevice *d, uint64_t addr);
uint16_t (*config_readw)(QVirtioDevice *d, uint64_t addr);
uint32_t (*config_readl)(QVirtioDevice *d, uint64_t addr);
uint64_t (*config_readq)(QVirtioDevice *d, uint64_t addr);
/* Get features of the device */
uint32_t (*get_features)(QVirtioDevice *d);
@ -144,13 +144,13 @@ static inline uint32_t qvring_size(uint32_t num, uint32_t align)
}
uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d,
void *addr);
uint64_t addr);
uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d,
void *addr);
uint64_t addr);
uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d,
void *addr);
uint64_t addr);
uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d,
void *addr);
uint64_t addr);
uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d);
void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d,
uint32_t features);

View File

@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.rc
. ./common.filter
_supported_fmt generic
_supported_fmt raw qcow qcow2 qed vdi vmdk vhdx
_supported_proto generic
_supported_os Linux

View File

@ -1,6 +0,0 @@
QA output created by 006
creating 128GB image
qemu-img: The image size is too large for file format 'vpc'
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=137438953472
*** done

View File

@ -43,6 +43,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow2
_supported_proto generic
_supported_os Linux
# refcount_bits must be at least 4 so we can create ten internal snapshots
# (1 bit supports none, 2 bits support two, 4 bits support 14)
_unsupported_imgopts 'refcount_bits=\(1\|2\)[^0-9]'
echo
echo "creating image"

View File

@ -43,6 +43,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow2
_supported_proto generic
_supported_os Linux
# Internal snapshots are (currently) impossible with refcount_bits=1
_unsupported_imgopts 'refcount_bits=1[^0-9]'
echo
echo "creating image"

View File

@ -46,6 +46,13 @@ _supported_proto file
_supported_os Linux
_default_cache_mode "writethrough"
_supported_cache_modes "writethrough" "none"
# The refcount table tests expect a certain minimum width for refcount entries
# (so that the refcount table actually needs to grow); that minimum is 16 bits,
# being the default refcount entry width.
# 32 and 64 bits do not work either, however, due to different leaked cluster
# count on error.
# Thus, the only remaining option is refcount_bits=16.
_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)'
echo "Errors while writing 128 kB"
echo

View File

@ -140,19 +140,13 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 5; imm: off; once: on; write
Failed to flush the L2 table cache: Input/output error
write failed: Input/output error
127 leaked clusters were found on the image.
This means waste of disk space, but no harm to data.
No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 5; imm: off; once: on; write -b
Failed to flush the L2 table cache: Input/output error
write failed: Input/output error
127 leaked clusters were found on the image.
This means waste of disk space, but no harm to data.
No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 5; imm: off; once: off; write
@ -174,19 +168,13 @@ This means waste of disk space, but no harm to data.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 28; imm: off; once: on; write
Failed to flush the L2 table cache: No space left on device
write failed: No space left on device
127 leaked clusters were found on the image.
This means waste of disk space, but no harm to data.
No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 28; imm: off; once: on; write -b
Failed to flush the L2 table cache: No space left on device
write failed: No space left on device
127 leaked clusters were found on the image.
This means waste of disk space, but no harm to data.
No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 28; imm: off; once: off; write
@ -356,13 +344,11 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_update_part; errno: 5; imm: off; once: on; write
Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_update_part; errno: 5; imm: off; once: on; write -b
Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
@ -382,13 +368,11 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_update_part; errno: 28; imm: off; once: on; write
Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_update_part; errno: 28; imm: off; once: on; write -b
Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824

View File

@ -44,6 +44,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow2
_supported_proto generic
_supported_os Linux
# Internal snapshots are (currently) impossible with refcount_bits=1
_unsupported_imgopts 'refcount_bits=1[^0-9]'
offset_size=24
offset_l1_size=36

View File

@ -4,90 +4,90 @@ QA output created by 049
== 1. Traditional size parameter ==
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024b
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1k
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1K
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1G
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1T
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0b
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5k
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5K
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5G
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5T
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
== 2. Specifying size via -o ==
qemu-img create -f qcow2 -o size=1024 TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1024b TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1k TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1K TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1M TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1G TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1T TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1024.0 TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1024.0b TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1.5k TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1.5K TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1.5M TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1.5G TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1.5T TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
== 3. Invalid sizes ==
@ -97,7 +97,7 @@ qemu-img: Image size must be less than 8 EiB!
qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2
qemu-img: qcow2 doesn't support shrinking images yet
qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k
qemu-img: Image size must be less than 8 EiB!
@ -105,14 +105,14 @@ qemu-img: Image size must be less than 8 EiB!
qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2
qemu-img: qcow2 doesn't support shrinking images yet
qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte
qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for
qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar
qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for
@ -125,84 +125,84 @@ qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2'
== Check correct interpretation of suffixes for cluster size ==
qemu-img create -f qcow2 -o cluster_size=1024 TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o cluster_size=1024b TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o cluster_size=1k TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o cluster_size=1K TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o cluster_size=1M TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1048576 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1048576 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o cluster_size=1024.0 TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o cluster_size=1024.0b TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o cluster_size=0.5k TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o cluster_size=0.5K TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o cluster_size=0.5M TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=524288 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=524288 lazy_refcounts=off refcount_bits=16
== Check compat level option ==
qemu-img create -f qcow2 -o compat=0.10 TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M
qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: '0.42'
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.42' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.42' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M
qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: 'foobar'
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='foobar' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='foobar' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
== Check preallocation option ==
qemu-img create -f qcow2 -o preallocation=off TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='off' lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='off' lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M
qemu-img: TEST_DIR/t.qcow2: invalid parameter value: 1234
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off refcount_bits=16
== Check encryption option ==
qemu-img create -f qcow2 -o encryption=off TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o encryption=on TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on cluster_size=65536 lazy_refcounts=off refcount_bits=16
== Check lazy_refcounts option (only with v3) ==
qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=off TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=on TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=on
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=on refcount_bits=16
qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M
qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=on
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=on refcount_bits=16
*** done

View File

@ -41,6 +41,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow2
_supported_proto file
_supported_os Linux
# A compat=0.10 image is created in this test which does not support anything
# other than refcount_bits=16
_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)'
function do_run_qemu()
{
@ -95,6 +98,12 @@ run_qemu -drive file="$TEST_IMG",driver=foo
run_qemu -drive file="$TEST_IMG",driver=raw,format=qcow2
run_qemu -drive file="$TEST_IMG",driver=qcow2,format=qcow2
echo
echo === Device without drive ===
echo
run_qemu -device virtio-scsi-pci -device scsi-hd
echo
echo === Overriding backing file ===
echo

View File

@ -47,6 +47,14 @@ Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format'
=== Device without drive ===
Testing: -device virtio-scsi-pci -device scsi-hd
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: -device scsi-hd: drive property not set
QEMU_PROG: -device scsi-hd: Device 'scsi-hd' could not be initialized
=== Overriding backing file ===
Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DIR/t.qcow2.orig -nodefaults
@ -115,8 +123,7 @@ QEMU X.Y.Z monitor - type 'help' for more information
Testing: -drive if=ide
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: Device needs media, but drive is empty
QEMU_PROG: Device initialization failed.
QEMU_PROG: Initialization of device ide-hd failed
QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed.
Testing: -drive if=virtio
QEMU X.Y.Z monitor - type 'help' for more information
@ -127,8 +134,7 @@ QEMU_PROG: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized
Testing: -drive if=scsi
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: -drive if=scsi: Device needs media, but drive is empty
QEMU_PROG: Device initialization failed.
QEMU_PROG: Initialization of device lsi53c895a failed
QEMU_PROG: Initialization of device lsi53c895a failed: Device initialization failed.
Testing: -drive if=none,id=disk -device ide-cd,drive=disk
QEMU X.Y.Z monitor - type 'help' for more information
@ -178,8 +184,7 @@ QEMU X.Y.Z monitor - type 'help' for more information
Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: Can't use a read-only drive
QEMU_PROG: Device initialization failed.
QEMU_PROG: Initialization of device ide-hd failed
QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed.
Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on
QEMU X.Y.Z monitor - type 'help' for more information

View File

@ -89,6 +89,8 @@ _supported_fmt qcow2
_supported_proto file
_supported_os Linux
_require_command QEMU_NBD
# Internal snapshots are (currently) impossible with refcount_bits=1
_unsupported_imgopts 'refcount_bits=1[^0-9]'
# Use -f raw instead of -f $IMGFMT for the NBD connection
QEMU_IO_NBD="$QEMU_IO -f raw --cache=$CACHEMODE"

View File

@ -18,6 +18,7 @@ cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
corrupt: true
qemu-io: can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write
read 512/512 bytes at offset 0

View File

@ -88,34 +88,41 @@ class TestQMP(TestImageInfoSpecific):
class TestQCow2(TestQemuImgInfo):
'''Testing a qcow2 version 2 image'''
img_options = 'compat=0.10'
json_compare = { 'compat': '0.10' }
human_compare = [ 'compat: 0.10' ]
json_compare = { 'compat': '0.10', 'refcount-bits': 16 }
human_compare = [ 'compat: 0.10', 'refcount bits: 16' ]
class TestQCow3NotLazy(TestQemuImgInfo):
'''Testing a qcow2 version 3 image with lazy refcounts disabled'''
img_options = 'compat=1.1,lazy_refcounts=off'
json_compare = { 'compat': '1.1', 'lazy-refcounts': False, 'corrupt': False }
human_compare = [ 'compat: 1.1', 'lazy refcounts: false', 'corrupt: false' ]
json_compare = { 'compat': '1.1', 'lazy-refcounts': False,
'refcount-bits': 16, 'corrupt': False }
human_compare = [ 'compat: 1.1', 'lazy refcounts: false',
'refcount bits: 16', 'corrupt: false' ]
class TestQCow3Lazy(TestQemuImgInfo):
'''Testing a qcow2 version 3 image with lazy refcounts enabled'''
img_options = 'compat=1.1,lazy_refcounts=on'
json_compare = { 'compat': '1.1', 'lazy-refcounts': True, 'corrupt': False }
human_compare = [ 'compat: 1.1', 'lazy refcounts: true', 'corrupt: false' ]
json_compare = { 'compat': '1.1', 'lazy-refcounts': True,
'refcount-bits': 16, 'corrupt': False }
human_compare = [ 'compat: 1.1', 'lazy refcounts: true',
'refcount bits: 16', 'corrupt: false' ]
class TestQCow3NotLazyQMP(TestQMP):
'''Testing a qcow2 version 3 image with lazy refcounts disabled, opening
with lazy refcounts enabled'''
img_options = 'compat=1.1,lazy_refcounts=off'
qemu_options = 'lazy-refcounts=on'
compare = { 'compat': '1.1', 'lazy-refcounts': False, 'corrupt': False }
compare = { 'compat': '1.1', 'lazy-refcounts': False,
'refcount-bits': 16, 'corrupt': False }
class TestQCow3LazyQMP(TestQMP):
'''Testing a qcow2 version 3 image with lazy refcounts enabled, opening
with lazy refcounts disabled'''
img_options = 'compat=1.1,lazy_refcounts=on'
qemu_options = 'lazy-refcounts=off'
compare = { 'compat': '1.1', 'lazy-refcounts': True, 'corrupt': False }
compare = { 'compat': '1.1', 'lazy-refcounts': True,
'refcount-bits': 16, 'corrupt': False }
TestImageInfoSpecific = None
TestQemuImgInfo = None

View File

@ -35,6 +35,8 @@ status=1 # failure is the default!
_supported_fmt qcow2
_supported_proto file
_supported_os Linux
# Because anything other than 16 would change the output of query-block
_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)'
function do_run_qemu()
{

View File

@ -32,6 +32,7 @@ Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virti
"data": {
"compat": "1.1",
"lazy-refcounts": false,
"refcount-bits": 16,
"corrupt": false
}
},
@ -208,6 +209,7 @@ Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk
"data": {
"compat": "1.1",
"lazy-refcounts": false,
"refcount-bits": 16,
"corrupt": false
}
},
@ -414,6 +416,7 @@ Testing:
"data": {
"compat": "1.1",
"lazy-refcounts": false,
"refcount-bits": 16,
"corrupt": false
}
},
@ -599,6 +602,7 @@ Testing:
"data": {
"compat": "1.1",
"lazy-refcounts": false,
"refcount-bits": 16,
"corrupt": false
}
},
@ -710,6 +714,7 @@ Testing:
"data": {
"compat": "1.1",
"lazy-refcounts": false,
"refcount-bits": 16,
"corrupt": false
}
},

View File

@ -42,19 +42,13 @@ _supported_fmt qcow2
_supported_proto file nfs
_supported_os Linux
function test_qemu_img()
{
echo qemu-img "$@" | _filter_testdir
$QEMU_IMG "$@" 2>&1 | _filter_testdir
echo
}
echo "=== Check option preallocation and cluster_size ==="
echo
cluster_sizes="16384 32768 65536 131072 262144 524288 1048576 2097152 4194304"
for s in $cluster_sizes; do
test_qemu_img create -f $IMGFMT -o preallocation=metadata,cluster_size=$s "$TEST_IMG" 4G
IMGOPTS=$(_optstr_add "$IMGOPTS" "preallocation=metadata,cluster_size=$s") \
_make_test_img 4G
done
# success, all done

View File

@ -1,32 +1,14 @@
QA output created by 079
=== Check option preallocation and cluster_size ===
qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=16384 TEST_DIR/t.qcow2 4G
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=16384 preallocation='metadata' lazy_refcounts=off
qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=32768 TEST_DIR/t.qcow2 4G
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=32768 preallocation='metadata' lazy_refcounts=off
qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=65536 TEST_DIR/t.qcow2 4G
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off
qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=131072 TEST_DIR/t.qcow2 4G
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=131072 preallocation='metadata' lazy_refcounts=off
qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=262144 TEST_DIR/t.qcow2 4G
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=262144 preallocation='metadata' lazy_refcounts=off
qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=524288 TEST_DIR/t.qcow2 4G
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=524288 preallocation='metadata' lazy_refcounts=off
qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=1048576 TEST_DIR/t.qcow2 4G
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=1048576 preallocation='metadata' lazy_refcounts=off
qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=2097152 TEST_DIR/t.qcow2 4G
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=2097152 preallocation='metadata' lazy_refcounts=off
qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=4194304 TEST_DIR/t.qcow2 4G
qemu-img: TEST_DIR/t.qcow2: Cluster size must be a power of two between 512 and 2048k
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=4194304 preallocation='metadata' lazy_refcounts=off
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata'
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata'
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata'
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata'
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata'
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata'
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata'
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata'
qemu-img: TEST_DIR/t.IMGFMT: Cluster size must be a power of two between 512 and 2048k
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata'
*** done

View File

@ -42,6 +42,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow2
_supported_proto file
_supported_os Linux
# Internal snapshots are (currently) impossible with refcount_bits=1
_unsupported_imgopts 'refcount_bits=1[^0-9]'
header_size=104

View File

@ -3,14 +3,14 @@ QA output created by 082
=== create: Options specified more than once ===
Testing: create -f foo -f qcow2 TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 128M (134217728 bytes)
cluster_size: 65536
Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=4096 lazy_refcounts=on
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=4096 lazy_refcounts=on refcount_bits=16
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 128M (134217728 bytes)
@ -18,10 +18,11 @@ cluster_size: 4096
Format specific information:
compat: 1.1
lazy refcounts: true
refcount bits: 16
corrupt: false
Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=on
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=on refcount_bits=16
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 128M (134217728 bytes)
@ -29,10 +30,11 @@ cluster_size: 8192
Format specific information:
compat: 1.1
lazy refcounts: true
refcount bits: 16
corrupt: false
Testing: create -f qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=off refcount_bits=16
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 128M (134217728 bytes)
@ -50,6 +52,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o ? TEST_DIR/t.qcow2 128M
@ -62,6 +65,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 128M
@ -74,6 +78,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 128M
@ -86,6 +91,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 128M
@ -98,6 +104,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 128M
@ -110,6 +117,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 128M
@ -122,6 +130,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 128M
@ -134,13 +143,14 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,help' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,help' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,?' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,?' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2 128M
qemu-img: Invalid option list: backing_file=TEST_DIR/t.qcow2,
@ -161,6 +171,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
Testing: create -o help
Supported options:
@ -169,7 +180,7 @@ size Virtual disk size
=== convert: Options specified more than once ===
Testing: create -f qcow2 TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
Testing: convert -f foo -f qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
image: TEST_DIR/t.IMGFMT.base
@ -190,6 +201,7 @@ cluster_size: 4096
Format specific information:
compat: 1.1
lazy refcounts: true
refcount bits: 16
corrupt: false
Testing: convert -O qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
@ -200,6 +212,7 @@ cluster_size: 8192
Format specific information:
compat: 1.1
lazy refcounts: true
refcount bits: 16
corrupt: false
Testing: convert -O qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
@ -220,6 +233,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
@ -232,6 +246,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
@ -244,6 +259,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
@ -256,6 +272,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
@ -268,6 +285,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
@ -280,6 +298,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
@ -292,6 +311,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
@ -304,6 +324,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
@ -331,6 +352,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
Testing: convert -o help
Supported options:
@ -346,6 +368,7 @@ cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: true
refcount bits: 16
corrupt: false
Testing: amend -f qcow2 -o size=130M -o lazy_refcounts=off TEST_DIR/t.qcow2
@ -356,6 +379,7 @@ cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
corrupt: false
Testing: amend -f qcow2 -o size=8M -o lazy_refcounts=on -o size=132M TEST_DIR/t.qcow2
@ -366,6 +390,7 @@ cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: true
refcount bits: 16
corrupt: false
Testing: amend -f qcow2 -o size=4M,size=148M TEST_DIR/t.qcow2
@ -386,6 +411,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2
@ -398,6 +424,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2
@ -410,6 +437,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2
@ -422,6 +450,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2
@ -434,6 +463,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2
@ -446,6 +476,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2
@ -458,6 +489,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2
@ -470,6 +502,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2
@ -499,6 +532,7 @@ encryption Encrypt the image
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
Testing: convert -o help
Supported options:

View File

@ -11,7 +11,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
=== Create a single snapshot on virtio0 ===
Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2.orig' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2.orig' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
{"return": {}}
=== Invalid command - missing device and nodename ===
@ -25,31 +25,31 @@ Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file
=== Create several transactional group snapshots ===
Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/1-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/1-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
{"return": {}}
Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
{"return": {}}
Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
{"return": {}}
Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
{"return": {}}
Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
{"return": {}}
Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
{"return": {}}
Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
{"return": {}}
Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
{"return": {}}
Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
{"return": {}}
*** done

View File

@ -41,6 +41,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow2
_supported_proto file
_supported_os Linux
# Because anything other than 16 would change the output of qemu_io -c info
_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)'
# Using an image filename containing quotation marks will render the JSON data
# below invalid. In that case, we have little choice but simply not to run this

View File

@ -43,6 +43,7 @@ vm state offset: 512 MiB
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
corrupt: false
format name: IMGFMT
cluster size: 64 KiB
@ -50,5 +51,6 @@ vm state offset: 512 MiB
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
corrupt: false
*** done

View File

@ -34,7 +34,7 @@ trap "exit \$status" 0 1 2 3 15
. ./common.rc
. ./common.filter
_supported_fmt generic
_supported_fmt raw qcow qcow2 qed vdi vmdk vhdx
_supported_proto generic
_supported_os Linux

View File

@ -43,6 +43,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow2
_supported_proto file
_supported_os Linux
# This test directly modifies a refblock so it relies on refcount_bits being 16
_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)'
echo
echo '=== Repairing an image without any refcount table ==='

187
tests/qemu-iotests/112 Executable file
View File

@ -0,0 +1,187 @@
#!/bin/bash
#
# Test cases for different refcount_bits values
#
# Copyright (C) 2015 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=mreitz@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
# This tests qcow2-specific low-level functionality
_supported_fmt qcow2
_supported_proto file
_supported_os Linux
# This test will set refcount_bits on its own which would conflict with the
# manual setting; compat will be overridden as well
_unsupported_imgopts refcount_bits 'compat=0.10'
function print_refcount_bits()
{
$QEMU_IMG info "$TEST_IMG" | sed -n '/refcount bits:/ s/^ *//p'
}
echo
echo '=== refcount_bits limits ==='
echo
# Must be positive (non-zero)
IMGOPTS="$IMGOPTS,refcount_bits=0" _make_test_img 64M
# Must be positive (non-negative)
IMGOPTS="$IMGOPTS,refcount_bits=-1" _make_test_img 64M
# May not exceed 64
IMGOPTS="$IMGOPTS,refcount_bits=128" _make_test_img 64M
# Must be a power of two
IMGOPTS="$IMGOPTS,refcount_bits=42" _make_test_img 64M
# 1 is the minimum
IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M
print_refcount_bits
# 64 is the maximum
IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M
print_refcount_bits
# 16 is the default
_make_test_img 64M
print_refcount_bits
echo
echo '=== refcount_bits and compat=0.10 ==='
echo
# Should work
IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=16" _make_test_img 64M
print_refcount_bits
# Should not work
IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=1" _make_test_img 64M
IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=64" _make_test_img 64M
echo
echo '=== Snapshot limit on refcount_bits=1 ==='
echo
IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M
print_refcount_bits
$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io
# Should fail for now; in the future, this might be supported by automatically
# copying all clusters with overflowing refcount
$QEMU_IMG snapshot -c foo "$TEST_IMG"
# The new L1 table could/should be leaked
_check_test_img
echo
echo '=== Snapshot limit on refcount_bits=2 ==='
echo
IMGOPTS="$IMGOPTS,refcount_bits=2" _make_test_img 64M
print_refcount_bits
$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io
# Should succeed
$QEMU_IMG snapshot -c foo "$TEST_IMG"
$QEMU_IMG snapshot -c bar "$TEST_IMG"
# Should fail (4th reference)
$QEMU_IMG snapshot -c baz "$TEST_IMG"
# The new L1 table could/should be leaked
_check_test_img
echo
echo '=== Compressed clusters with refcount_bits=1 ==='
echo
IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M
print_refcount_bits
# Both should fit into a single host cluster; instead of failing to increase the
# refcount of that cluster, qemu should just allocate a new cluster and make
# this operation succeed
$QEMU_IO -c 'write -P 0 -c 0 64k' \
-c 'write -P 1 -c 64k 64k' \
"$TEST_IMG" | _filter_qemu_io
_check_test_img
echo
echo '=== MSb set in 64 bit refcount ==='
echo
IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M
print_refcount_bits
$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io
# Set the MSb in the refblock entry of the data cluster
poke_file "$TEST_IMG" $((0x20028)) "\x80\x00\x00\x00\x00\x00\x00\x00"
# Clear OFLAG_COPIED in the L2 entry of the data cluster
poke_file "$TEST_IMG" $((0x40000)) "\x00\x00\x00\x00\x00\x05\x00\x00"
# Try to write to that cluster (should work, even though the MSb is set)
$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io
echo
echo '=== Snapshot on maximum 64 bit refcount value ==='
echo
IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M
print_refcount_bits
$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io
# Set the refblock entry to the maximum value possible
poke_file "$TEST_IMG" $((0x20028)) "\xff\xff\xff\xff\xff\xff\xff\xff"
# Clear OFLAG_COPIED in the L2 entry of the data cluster
poke_file "$TEST_IMG" $((0x40000)) "\x00\x00\x00\x00\x00\x05\x00\x00"
# Try a snapshot (should correctly identify the overflow; may work in the future
# by falling back to COW)
$QEMU_IMG snapshot -c foo "$TEST_IMG"
# The new L1 table could/should be leaked; and obviously the data cluster is
# leaked (refcount=UINT64_MAX reference=1)
_check_test_img
# success, all done
echo '*** done'
rm -f $seq.full
status=0

View File

@ -0,0 +1,84 @@
QA output created by 112
=== refcount_bits limits ===
qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 refcount_bits=-1
qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
refcount bits: 1
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
refcount bits: 64
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
refcount bits: 16
=== refcount_bits and compat=0.10 ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
refcount bits: 16
qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater)
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater)
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
=== Snapshot limit on refcount_bits=1 ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
refcount bits: 1
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qemu-img: Could not create snapshot 'foo': -22 (Invalid argument)
Leaked cluster 6 refcount=1 reference=0
1 leaked clusters were found on the image.
This means waste of disk space, but no harm to data.
=== Snapshot limit on refcount_bits=2 ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
refcount bits: 2
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qemu-img: Could not create snapshot 'baz': -22 (Invalid argument)
Leaked cluster 7 refcount=1 reference=0
1 leaked clusters were found on the image.
This means waste of disk space, but no harm to data.
=== Compressed clusters with refcount_bits=1 ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
refcount bits: 1
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 65536
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
=== MSb set in 64 bit refcount ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
refcount bits: 64
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 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Snapshot on maximum 64 bit refcount value ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
refcount bits: 64
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qemu-img: Could not create snapshot 'foo': -22 (Invalid argument)
Leaked cluster 5 refcount=18446744073709551615 reference=1
Leaked cluster 6 refcount=1 reference=0
2 leaked clusters were found on the image.
This means waste of disk space, but no harm to data.
*** done

View File

@ -1,9 +1,8 @@
#!/bin/bash
#
# Make sure qemu-img rejects > 127GB images for the vpc format as the format
# doesn't support this.
# Test that opening O_DIRECT succeeds when image file I/O produces EIO
#
# Copyright (C) 2009 Red Hat, Inc.
# Copyright (C) 2015 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
@ -20,7 +19,7 @@
#
# creator
owner=hch@lst.de
owner=stefanha@redhat.com
seq=`basename $0`
echo "QA output created by $seq"
@ -29,9 +28,34 @@ here=`pwd`
tmp=/tmp/$$
status=1 # failure is the default!
devname="eiodev$$"
_setup_eiodev()
{
# This test should either be run as root or with passwordless sudo
for cmd in "" "sudo -n"; do
echo "0 $((1024 * 1024 * 1024 / 512)) error" | \
$cmd dmsetup create "$devname" 2>/dev/null
if [ "$?" -eq 0 ]; then
return
fi
done
_notrun "root privileges required to run dmsetup"
}
_cleanup_eiodev()
{
for cmd in "" "sudo -n"; do
$cmd dmsetup remove "$devname" 2>/dev/null
if [ "$?" -eq 0 ]; then
return
fi
done
}
_cleanup()
{
_cleanup_test_img
_cleanup_eiodev
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@ -39,14 +63,18 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.rc
. ./common.filter
_supported_fmt vpc
_supported_proto generic
_supported_fmt raw
_supported_proto file
_supported_os Linux
_setup_eiodev
TEST_IMG="/dev/mapper/$devname"
echo
echo "creating 128GB image"
_make_test_img 128G
echo "== reading from error device =="
# Opening image should succeed but the read operation should fail
$QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io
# success, all done
echo "*** done"

View File

@ -0,0 +1,5 @@
QA output created by 128
== reading from error device ==
read failed: Input/output error
*** done

View File

@ -192,7 +192,8 @@ _filter_img_create()
-e "s# block_size=[0-9]\\+##g" \
-e "s# block_state_zero=\\(on\\|off\\)##g" \
-e "s# log_size=[0-9]\\+##g" \
-e "s/archipelago:a/TEST_DIR\//g"
-e "s/archipelago:a/TEST_DIR\//g" \
-e "s# refcount_bits=[0-9]\\+##g"
}
_filter_img_info()

View File

@ -12,7 +12,7 @@
003 rw auto
004 rw auto quick
005 img auto quick
006 img auto
# 006 was removed, do not reuse
007 snapshot auto
008 rw auto quick
009 rw auto quick
@ -116,7 +116,9 @@
109 rw auto
110 rw auto backing quick
111 rw auto quick
112 rw auto
113 rw auto quick
114 rw auto quick
116 rw auto quick
123 rw auto quick
128 rw auto quick

View File

@ -13,6 +13,7 @@
#include <glib.h>
#include "block/coroutine.h"
#include "block/coroutine_int.h"
/*
* Check that qemu_in_coroutine() works
@ -122,6 +123,30 @@ static void test_yield(void)
g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */
}
static void coroutine_fn c2_fn(void *opaque)
{
qemu_coroutine_yield();
}
static void coroutine_fn c1_fn(void *opaque)
{
Coroutine *c2 = opaque;
qemu_coroutine_enter(c2, NULL);
}
static void test_co_queue(void)
{
Coroutine *c1;
Coroutine *c2;
c1 = qemu_coroutine_create(c1_fn);
c2 = qemu_coroutine_create(c2_fn);
qemu_coroutine_enter(c1, c2);
memset(c1, 0xff, sizeof(Coroutine));
qemu_coroutine_enter(c2, NULL);
}
/*
* Check that creation, enter, and return work
*/
@ -343,6 +368,7 @@ static void perf_cost(void)
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/basic/co_queue", test_co_queue);
g_test_add_func("/basic/lifecycle", test_lifecycle);
g_test_add_func("/basic/yield", test_yield);
g_test_add_func("/basic/nesting", test_nesting);

View File

@ -16,9 +16,11 @@
#include "libqtest.h"
#include "libqos/virtio.h"
#include "libqos/virtio-pci.h"
#include "libqos/virtio-mmio.h"
#include "libqos/pci-pc.h"
#include "libqos/malloc.h"
#include "libqos/malloc-pc.h"
#include "libqos/malloc-generic.h"
#include "qemu/bswap.h"
#define QVIRTIO_BLK_F_BARRIER 0x00000001
@ -42,10 +44,14 @@
#define TEST_IMAGE_SIZE (64 * 1024 * 1024)
#define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000)
#define PCI_SLOT_HP 0x06
#define PCI_SLOT 0x04
#define PCI_FN 0x00
#define PCI_SLOT_HP 0x06
#define MMIO_PAGE_SIZE 4096
#define MMIO_DEV_BASE_ADDR 0x0A003E00
#define MMIO_RAM_ADDR 0x40000000
#define MMIO_RAM_SIZE 0x20000000
typedef struct QVirtioBlkReq {
uint32_t type;
@ -55,11 +61,10 @@ typedef struct QVirtioBlkReq {
uint8_t status;
} QVirtioBlkReq;
static QPCIBus *test_start(void)
static char *drive_create(void)
{
char *cmdline;
char tmp_path[] = "/tmp/qtest.XXXXXX";
int fd, ret;
char *tmp_path = g_strdup("/tmp/qtest.XXXXXX");
/* Create a temporary raw image */
fd = mkstemp(tmp_path);
@ -68,24 +73,52 @@ static QPCIBus *test_start(void)
g_assert_cmpint(ret, ==, 0);
close(fd);
return tmp_path;
}
static QPCIBus *pci_test_start(void)
{
char *cmdline;
char *tmp_path;
tmp_path = drive_create();
cmdline = g_strdup_printf("-drive if=none,id=drive0,file=%s,format=raw "
"-drive if=none,id=drive1,file=/dev/null,format=raw "
"-device virtio-blk-pci,id=drv0,drive=drive0,"
"addr=%x.%x",
tmp_path, PCI_SLOT, PCI_FN);
"-drive if=none,id=drive1,file=/dev/null,format=raw "
"-device virtio-blk-pci,id=drv0,drive=drive0,"
"addr=%x.%x",
tmp_path, PCI_SLOT, PCI_FN);
qtest_start(cmdline);
unlink(tmp_path);
g_free(tmp_path);
g_free(cmdline);
return qpci_init_pc();
}
static void arm_test_start(void)
{
char *cmdline;
char *tmp_path;
tmp_path = drive_create();
cmdline = g_strdup_printf("-machine virt "
"-drive if=none,id=drive0,file=%s,format=raw "
"-device virtio-blk-device,drive=drive0",
tmp_path);
qtest_start(cmdline);
unlink(tmp_path);
g_free(tmp_path);
g_free(cmdline);
}
static void test_end(void)
{
qtest_end();
}
static QVirtioPCIDevice *virtio_blk_init(QPCIBus *bus, int slot)
static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot)
{
QVirtioPCIDevice *dev;
@ -135,14 +168,10 @@ static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req,
return addr;
}
static void pci_basic(void)
static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev,
QGuestAllocator *alloc, QVirtQueue *vq, uint64_t device_specific)
{
QVirtioPCIDevice *dev;
QPCIBus *bus;
QVirtQueuePCI *vqpci;
QGuestAllocator *alloc;
QVirtioBlkReq req;
void *addr;
uint64_t req_addr;
uint64_t capacity;
uint32_t features;
@ -150,96 +179,37 @@ static void pci_basic(void)
uint8_t status;
char *data;
bus = test_start();
capacity = qvirtio_config_readq(bus, dev, device_specific);
dev = virtio_blk_init(bus, PCI_SLOT);
/* MSI-X is not enabled */
addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX;
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
features = qvirtio_get_features(bus, dev);
features = features & ~(QVIRTIO_F_BAD_FEATURE |
QVIRTIO_F_RING_INDIRECT_DESC | QVIRTIO_F_RING_EVENT_IDX |
QVIRTIO_BLK_F_SCSI);
qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
qvirtio_set_features(bus, dev, features);
alloc = pc_alloc_init();
vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
alloc, 0);
qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
/* Write and read with 2 descriptor layout */
/* Write request */
req.type = QVIRTIO_BLK_T_OUT;
req.ioprio = 1;
req.sector = 0;
req.data = g_malloc0(512);
strcpy(req.data, "TEST");
req_addr = virtio_blk_request(alloc, &req, 512);
g_free(req.data);
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
g_assert_cmpint(status, ==, 0);
guest_free(alloc, req_addr);
/* Read request */
req.type = QVIRTIO_BLK_T_IN;
req.ioprio = 1;
req.sector = 0;
req.data = g_malloc0(512);
req_addr = virtio_blk_request(alloc, &req, 512);
g_free(req.data);
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false);
qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
g_assert_cmpint(status, ==, 0);
data = g_malloc0(512);
memread(req_addr + 16, data, 512);
g_assert_cmpstr(data, ==, "TEST");
g_free(data);
guest_free(alloc, req_addr);
qvirtio_set_driver_ok(bus, dev);
/* Write and read with 3 descriptor layout */
/* Write request */
req.type = QVIRTIO_BLK_T_OUT;
req.ioprio = 1;
req.sector = 1;
req.sector = 0;
req.data = g_malloc0(512);
strcpy(req.data, "TEST");
req_addr = virtio_blk_request(alloc, &req, 512);
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
g_free(req.data);
qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
qvirtqueue_add(vq, req_addr + 16, 512, false, true);
qvirtqueue_add(vq, req_addr + 528, 1, true, false);
qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
QVIRTIO_BLK_TIMEOUT_US);
qvirtqueue_kick(bus, dev, vq, free_head);
qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
g_assert_cmpint(status, ==, 0);
@ -248,21 +218,20 @@ static void pci_basic(void)
/* Read request */
req.type = QVIRTIO_BLK_T_IN;
req.ioprio = 1;
req.sector = 1;
req.sector = 0;
req.data = g_malloc0(512);
req_addr = virtio_blk_request(alloc, &req, 512);
g_free(req.data);
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true);
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
qvirtqueue_add(vq, req_addr + 16, 512, true, true);
qvirtqueue_add(vq, req_addr + 528, 1, true, false);
qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
qvirtqueue_kick(bus, dev, vq, free_head);
qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
QVIRTIO_BLK_TIMEOUT_US);
qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
g_assert_cmpint(status, ==, 0);
@ -273,10 +242,84 @@ static void pci_basic(void)
guest_free(alloc, req_addr);
if (features & QVIRTIO_F_ANY_LAYOUT) {
/* Write and read with 2 descriptor layout */
/* Write request */
req.type = QVIRTIO_BLK_T_OUT;
req.ioprio = 1;
req.sector = 1;
req.data = g_malloc0(512);
strcpy(req.data, "TEST");
req_addr = virtio_blk_request(alloc, &req, 512);
g_free(req.data);
free_head = qvirtqueue_add(vq, req_addr, 528, false, true);
qvirtqueue_add(vq, req_addr + 528, 1, true, false);
qvirtqueue_kick(bus, dev, vq, free_head);
qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
g_assert_cmpint(status, ==, 0);
guest_free(alloc, req_addr);
/* Read request */
req.type = QVIRTIO_BLK_T_IN;
req.ioprio = 1;
req.sector = 1;
req.data = g_malloc0(512);
req_addr = virtio_blk_request(alloc, &req, 512);
g_free(req.data);
free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
qvirtqueue_add(vq, req_addr + 16, 513, true, false);
qvirtqueue_kick(bus, dev, vq, free_head);
qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
g_assert_cmpint(status, ==, 0);
data = g_malloc0(512);
memread(req_addr + 16, data, 512);
g_assert_cmpstr(data, ==, "TEST");
g_free(data);
guest_free(alloc, req_addr);
}
}
static void pci_basic(void)
{
QVirtioPCIDevice *dev;
QPCIBus *bus;
QVirtQueuePCI *vqpci;
QGuestAllocator *alloc;
void *addr;
bus = pci_test_start();
dev = virtio_blk_pci_init(bus, PCI_SLOT);
alloc = pc_alloc_init();
vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
alloc, 0);
/* MSI-X is not enabled */
addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX;
test_basic(&qvirtio_pci, &dev->vdev, alloc, &vqpci->vq,
(uint64_t)(uintptr_t)addr);
/* End test */
guest_free(alloc, vqpci->vq.desc);
pc_alloc_uninit(alloc);
qvirtio_pci_device_disable(dev);
g_free(dev);
qpci_free_pc(bus);
test_end();
}
@ -296,14 +339,15 @@ static void pci_indirect(void)
uint8_t status;
char *data;
bus = test_start();
bus = pci_test_start();
dev = virtio_blk_init(bus, PCI_SLOT);
dev = virtio_blk_pci_init(bus, PCI_SLOT);
/* MSI-X is not enabled */
addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX;
addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX;
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
(uint64_t)(uintptr_t)addr);
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
@ -374,8 +418,10 @@ static void pci_indirect(void)
/* End test */
guest_free(alloc, vqpci->vq.desc);
pc_alloc_uninit(alloc);
qvirtio_pci_device_disable(dev);
g_free(dev);
qpci_free_pc(bus);
test_end();
}
@ -387,14 +433,15 @@ static void pci_config(void)
void *addr;
uint64_t capacity;
bus = test_start();
bus = pci_test_start();
dev = virtio_blk_init(bus, PCI_SLOT);
dev = virtio_blk_pci_init(bus, PCI_SLOT);
/* MSI-X is not enabled */
addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX;
addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX;
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
(uint64_t)(uintptr_t)addr);
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
@ -403,11 +450,13 @@ static void pci_config(void)
" 'size': %d } }", n_size);
qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US);
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
(uint64_t)(uintptr_t)addr);
g_assert_cmpint(capacity, ==, n_size / 512);
qvirtio_pci_device_disable(dev);
g_free(dev);
qpci_free_pc(bus);
test_end();
}
@ -427,18 +476,19 @@ static void pci_msix(void)
uint8_t status;
char *data;
bus = test_start();
bus = pci_test_start();
alloc = pc_alloc_init();
dev = virtio_blk_init(bus, PCI_SLOT);
dev = virtio_blk_pci_init(bus, PCI_SLOT);
qpci_msix_enable(dev->pdev);
qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0);
/* MSI-X is enabled */
addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_MSIX;
addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX;
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
(uint64_t)(uintptr_t)addr);
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
@ -458,7 +508,8 @@ static void pci_msix(void)
qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US);
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
(uint64_t)(uintptr_t)addr);
g_assert_cmpint(capacity, ==, n_size / 512);
/* Write request */
@ -472,7 +523,8 @@ static void pci_msix(void)
g_free(req.data);
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true);
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
@ -495,7 +547,8 @@ static void pci_msix(void)
g_free(req.data);
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false);
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true);
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
@ -514,10 +567,12 @@ static void pci_msix(void)
guest_free(alloc, req_addr);
/* End test */
guest_free(alloc, (uint64_t)vqpci->vq.desc);
guest_free(alloc, vqpci->vq.desc);
pc_alloc_uninit(alloc);
qpci_msix_disable(dev->pdev);
qvirtio_pci_device_disable(dev);
g_free(dev);
qpci_free_pc(bus);
test_end();
}
@ -536,18 +591,19 @@ static void pci_idx(void)
uint8_t status;
char *data;
bus = test_start();
bus = pci_test_start();
alloc = pc_alloc_init();
dev = virtio_blk_init(bus, PCI_SLOT);
dev = virtio_blk_pci_init(bus, PCI_SLOT);
qpci_msix_enable(dev->pdev);
qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0);
/* MSI-X is enabled */
addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_MSIX;
addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX;
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
(uint64_t)(uintptr_t)addr);
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
@ -573,7 +629,8 @@ static void pci_idx(void)
g_free(req.data);
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true);
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
@ -593,7 +650,8 @@ static void pci_idx(void)
/* Notify after processing the third request */
qvirtqueue_set_used_event(&vqpci->vq, 2);
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true);
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
@ -616,11 +674,11 @@ static void pci_idx(void)
g_free(req.data);
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false);
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true);
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
QVIRTIO_BLK_TIMEOUT_US);
@ -636,45 +694,94 @@ static void pci_idx(void)
/* End test */
guest_free(alloc, vqpci->vq.desc);
pc_alloc_uninit(alloc);
qpci_msix_disable(dev->pdev);
qvirtio_pci_device_disable(dev);
g_free(dev);
qpci_free_pc(bus);
test_end();
}
static void hotplug(void)
static void pci_hotplug(void)
{
QPCIBus *bus;
QVirtioPCIDevice *dev;
bus = test_start();
bus = pci_test_start();
/* plug secondary disk */
qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP,
"'drive': 'drive1'");
dev = virtio_blk_init(bus, PCI_SLOT_HP);
dev = virtio_blk_pci_init(bus, PCI_SLOT_HP);
g_assert(dev);
qvirtio_pci_device_disable(dev);
g_free(dev);
/* unplug secondary disk */
qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP);
qpci_free_pc(bus);
test_end();
}
static void mmio_basic(void)
{
QVirtioMMIODevice *dev;
QVirtQueue *vq;
QGuestAllocator *alloc;
int n_size = TEST_IMAGE_SIZE / 2;
uint64_t capacity;
arm_test_start();
dev = qvirtio_mmio_init_device(MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE);
g_assert(dev != NULL);
g_assert_cmphex(dev->vdev.device_type, ==, QVIRTIO_BLK_DEVICE_ID);
qvirtio_reset(&qvirtio_mmio, &dev->vdev);
qvirtio_set_acknowledge(&qvirtio_mmio, &dev->vdev);
qvirtio_set_driver(&qvirtio_mmio, &dev->vdev);
alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE);
vq = qvirtqueue_setup(&qvirtio_mmio, &dev->vdev, alloc, 0);
test_basic(&qvirtio_mmio, &dev->vdev, alloc, vq,
QVIRTIO_MMIO_DEVICE_SPECIFIC);
qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', "
" 'size': %d } }", n_size);
qvirtio_wait_queue_isr(&qvirtio_mmio, &dev->vdev, vq,
QVIRTIO_BLK_TIMEOUT_US);
capacity = qvirtio_config_readq(&qvirtio_mmio, &dev->vdev,
QVIRTIO_MMIO_DEVICE_SPECIFIC);
g_assert_cmpint(capacity, ==, n_size / 512);
/* End test */
guest_free(alloc, vq->desc);
generic_alloc_uninit(alloc);
g_free(dev);
test_end();
}
int main(int argc, char **argv)
{
int ret;
const char *arch = qtest_get_arch();
g_test_init(&argc, &argv, NULL);
g_test_add_func("/virtio/blk/pci/basic", pci_basic);
g_test_add_func("/virtio/blk/pci/indirect", pci_indirect);
g_test_add_func("/virtio/blk/pci/config", pci_config);
g_test_add_func("/virtio/blk/pci/msix", pci_msix);
g_test_add_func("/virtio/blk/pci/idx", pci_idx);
g_test_add_func("/virtio/blk/pci/hotplug", hotplug);
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
qtest_add_func("/virtio/blk/pci/basic", pci_basic);
qtest_add_func("/virtio/blk/pci/indirect", pci_indirect);
qtest_add_func("/virtio/blk/pci/config", pci_config);
qtest_add_func("/virtio/blk/pci/msix", pci_msix);
qtest_add_func("/virtio/blk/pci/idx", pci_idx);
qtest_add_func("/virtio/blk/pci/hotplug", pci_hotplug);
} else if (strcmp(arch, "arm") == 0) {
qtest_add_func("/virtio/blk/mmio/basic", mmio_basic);
}
ret = g_test_run();