mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-27 21:40:49 +00:00
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:
commit
1976058109
63
MAINTAINERS
63
MAINTAINERS
@ -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
34
block.c
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
111
block/qcow2.c
111
block/qcow2.c
@ -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 */ }
|
||||
}
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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, §or_size) >= 0) {
|
||||
*sector_size_p = sector_size;
|
||||
success = true;
|
||||
}
|
||||
#endif
|
||||
#ifdef DKIOCGETBLOCKSIZE
|
||||
if (ioctl(fd, DKIOCGETBLOCKSIZE, §or_size) >= 0) {
|
||||
*sector_size_p = sector_size;
|
||||
success = true;
|
||||
}
|
||||
#endif
|
||||
#ifdef DIOCGSECTORSIZE
|
||||
if (ioctl(fd, DIOCGSECTORSIZE, §or_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, §or_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, §or_size) >= 0) {
|
||||
bs->request_alignment = sector_size;
|
||||
}
|
||||
#endif
|
||||
#ifdef DIOCGSECTORSIZE
|
||||
if (ioctl(fd, DIOCGSECTORSIZE, §or_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,
|
||||
|
@ -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,
|
||||
|
171
block/sheepdog.c
171
block/sheepdog.c
@ -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 */ }
|
||||
}
|
||||
};
|
||||
|
25
block/vdi.c
25
block/vdi.c
@ -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);
|
||||
}
|
||||
|
||||
|
60
block/vpc.c
60
block/vpc.c
@ -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,
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
118
hw/ide/core.c
118
hw/ide/core.c
@ -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()
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
109
hw/ide/pci.c
109
hw/ide/pci.c
@ -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,
|
||||
};
|
||||
|
||||
|
12
hw/ide/pci.h
12
hw/ide/pci.h
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
typedef enum {
|
||||
COROUTINE_YIELD = 1,
|
||||
COROUTINE_TERMINATE = 2,
|
||||
COROUTINE_ENTER = 3,
|
||||
} CoroutineAction;
|
||||
|
||||
struct Coroutine {
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
} }
|
||||
|
||||
##
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
39
tests/libqos/malloc-generic.c
Normal file
39
tests/libqos/malloc-generic.c
Normal 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);
|
||||
}
|
21
tests/libqos/malloc-generic.h
Normal file
21
tests/libqos/malloc-generic.h
Normal 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
198
tests/libqos/virtio-mmio.c
Normal 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;
|
||||
}
|
46
tests/libqos/virtio-mmio.h
Normal file
46
tests/libqos/virtio-mmio.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
}
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
187
tests/qemu-iotests/112
Executable 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
|
84
tests/qemu-iotests/112.out
Normal file
84
tests/qemu-iotests/112.out
Normal 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
|
@ -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"
|
5
tests/qemu-iotests/128.out
Normal file
5
tests/qemu-iotests/128.out
Normal file
@ -0,0 +1,5 @@
|
||||
QA output created by 128
|
||||
|
||||
== reading from error device ==
|
||||
read failed: Input/output error
|
||||
*** done
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user