mirror of https://github.com/xemu-project/xemu.git
Pull request
v2: * Fix C11 typedef redefinitions in ahci and libqos malloc [Peter] * Fix lx -> PRIx64 format specifiers in ahci [Peter] -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJU4hCxAAoJEJykq7OBq3PIh2EH/02LojwlJwtsUoFuDDOdqVu+ iQsGbGpmtjeaBkiiiQnlN0UHFJk+I05JohagzivchB6p1v0BDnGPMGCRiY2a/URU ZrBt6kvIL6ro6n6ebrMq4+MFbyfgXC6tC8UuBnHBOlJMArHCue8w3BD+FWyYOJGY +zzvWKSNxEkjwPyXuBPJI0eqLCmANnr31LVrh+4gTvXRnpArT+fNTBooCgA6zsRe ChHObhstM/ljy7XZz5TmTXFf/68+WM8AYF+xIHiU3olCcBgH8VPn7lsdBSEuOtxH HNe9tt2M91R+3Zdr2KXu+D4BRARKMlft9GDwLGAvGrYcBr7NNJo3KbKbxoecw4M= =2rE4 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/stefanha/tags/block-pull-request' into staging Pull request v2: * Fix C11 typedef redefinitions in ahci and libqos malloc [Peter] * Fix lx -> PRIx64 format specifiers in ahci [Peter] # gpg: Signature made Mon Feb 16 15:45:53 2015 GMT using RSA key ID 81AB73C8 # gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>" # gpg: aka "Stefan Hajnoczi <stefanha@gmail.com>" * remotes/stefanha/tags/block-pull-request: (65 commits) block: Keep bdrv_check*_request()'s return value block: Remove "growable" from BDS block: Clamp BlockBackend requests qemu-io: Use BlockBackend qemu-io: Remove "growable" option qemu-io: Use blk_new_open() in openfile() qemu-nbd: Use blk_new_open() in main() qemu-img: Use BlockBackend as far as possible qemu-img: Use blk_new_open() in img_rebase() qemu-img: Use blk_new_open() in img_open() block/xen: Use blk_new_open() in blk_connect() blockdev: Use blk_new_open() in blockdev_init() iotests: Add test for driver=qcow2, format=qcow2 block: Add Error parameter to bdrv_find_protocol() block: Add blk_new_open() block: Lift some BDS functions to the BlockBackend iotests: Add test for qemu-img convert to NBD qemu-img: Fix qemu-img convert -n qemu-iotests: Add 093 for IO throttling qemu-iotests: Allow caller to disable underscore convertion for qmp ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
3dc10613c3
73
block.c
73
block.c
|
@ -508,9 +508,8 @@ int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
|
|||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
drv = bdrv_find_protocol(filename, true);
|
||||
drv = bdrv_find_protocol(filename, true, errp);
|
||||
if (drv == NULL) {
|
||||
error_setg(errp, "Could not find protocol for file '%s'", filename);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
@ -628,7 +627,8 @@ static BlockDriver *find_hdev_driver(const char *filename)
|
|||
}
|
||||
|
||||
BlockDriver *bdrv_find_protocol(const char *filename,
|
||||
bool allow_protocol_prefix)
|
||||
bool allow_protocol_prefix,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriver *drv1;
|
||||
char protocol[128];
|
||||
|
@ -666,6 +666,8 @@ BlockDriver *bdrv_find_protocol(const char *filename,
|
|||
return drv1;
|
||||
}
|
||||
}
|
||||
|
||||
error_setg(errp, "Unknown protocol '%s'", protocol);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -970,7 +972,6 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
|||
bs->zero_beyond_eof = true;
|
||||
open_flags = bdrv_open_flags(bs, flags);
|
||||
bs->read_only = !(open_flags & BDRV_O_RDWR);
|
||||
bs->growable = !!(flags & BDRV_O_PROTOCOL);
|
||||
|
||||
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
|
||||
error_setg(errp,
|
||||
|
@ -1136,9 +1137,8 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
|
|||
} else {
|
||||
if (!drvname && protocol) {
|
||||
if (filename) {
|
||||
drv = bdrv_find_protocol(filename, parse_filename);
|
||||
drv = bdrv_find_protocol(filename, parse_filename, errp);
|
||||
if (!drv) {
|
||||
error_setg(errp, "Unknown protocol");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -1885,7 +1885,6 @@ void bdrv_close(BlockDriverState *bs)
|
|||
bs->encrypted = 0;
|
||||
bs->valid_key = 0;
|
||||
bs->sg = 0;
|
||||
bs->growable = 0;
|
||||
bs->zero_beyond_eof = false;
|
||||
QDECREF(bs->options);
|
||||
bs->options = NULL;
|
||||
|
@ -2645,25 +2644,17 @@ exit:
|
|||
static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
|
||||
size_t size)
|
||||
{
|
||||
int64_t len;
|
||||
|
||||
if (size > BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!bdrv_is_inserted(bs))
|
||||
if (!bdrv_is_inserted(bs)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
if (bs->growable)
|
||||
return 0;
|
||||
|
||||
len = bdrv_getlength(bs);
|
||||
|
||||
if (offset < 0)
|
||||
return -EIO;
|
||||
|
||||
if ((offset > len) || (len - offset < size))
|
||||
if (offset < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -3042,10 +3033,10 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs,
|
|||
}
|
||||
|
||||
/* Forward the request to the BlockDriver */
|
||||
if (!(bs->zero_beyond_eof && bs->growable)) {
|
||||
if (!bs->zero_beyond_eof) {
|
||||
ret = drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov);
|
||||
} else {
|
||||
/* Read zeros after EOF of growable BDSes */
|
||||
/* Read zeros after EOF */
|
||||
int64_t total_sectors, max_nb_sectors;
|
||||
|
||||
total_sectors = bdrv_nb_sectors(bs);
|
||||
|
@ -3107,8 +3098,10 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs,
|
|||
if (!drv) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
if (bdrv_check_byte_request(bs, offset, bytes)) {
|
||||
return -EIO;
|
||||
|
||||
ret = bdrv_check_byte_request(bs, offset, bytes);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (bs->copy_on_read) {
|
||||
|
@ -3322,7 +3315,7 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
|
|||
|
||||
block_acct_highest_sector(&bs->stats, sector_num, nb_sectors);
|
||||
|
||||
if (bs->growable && ret >= 0) {
|
||||
if (ret >= 0) {
|
||||
bs->total_sectors = MAX(bs->total_sectors, sector_num + nb_sectors);
|
||||
}
|
||||
|
||||
|
@ -3351,8 +3344,10 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
|
|||
if (bs->read_only) {
|
||||
return -EACCES;
|
||||
}
|
||||
if (bdrv_check_byte_request(bs, offset, bytes)) {
|
||||
return -EIO;
|
||||
|
||||
ret = bdrv_check_byte_request(bs, offset, bytes);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* throttling disk I/O */
|
||||
|
@ -4206,12 +4201,18 @@ int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
|||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (!drv)
|
||||
int ret;
|
||||
|
||||
if (!drv) {
|
||||
return -ENOMEDIUM;
|
||||
if (!drv->bdrv_write_compressed)
|
||||
}
|
||||
if (!drv->bdrv_write_compressed) {
|
||||
return -ENOTSUP;
|
||||
if (bdrv_check_request(bs, sector_num, nb_sectors))
|
||||
return -EIO;
|
||||
}
|
||||
ret = bdrv_check_request(bs, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
assert(QLIST_EMPTY(&bs->dirty_bitmaps));
|
||||
|
||||
|
@ -5126,12 +5127,15 @@ static void coroutine_fn bdrv_discard_co_entry(void *opaque)
|
|||
int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
int max_discard;
|
||||
int max_discard, ret;
|
||||
|
||||
if (!bs->drv) {
|
||||
return -ENOMEDIUM;
|
||||
} else if (bdrv_check_request(bs, sector_num, nb_sectors)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = bdrv_check_request(bs, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
} else if (bs->read_only) {
|
||||
return -EROFS;
|
||||
}
|
||||
|
@ -5623,9 +5627,8 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
|||
return;
|
||||
}
|
||||
|
||||
proto_drv = bdrv_find_protocol(filename, true);
|
||||
proto_drv = bdrv_find_protocol(filename, true, errp);
|
||||
if (!proto_drv) {
|
||||
error_setg(errp, "Unknown protocol '%s'", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,16 @@ struct BlockBackend {
|
|||
void *dev_opaque;
|
||||
};
|
||||
|
||||
typedef struct BlockBackendAIOCB {
|
||||
BlockAIOCB common;
|
||||
QEMUBH *bh;
|
||||
int ret;
|
||||
} BlockBackendAIOCB;
|
||||
|
||||
static const AIOCBInfo block_backend_aiocb_info = {
|
||||
.aiocb_size = sizeof(BlockBackendAIOCB),
|
||||
};
|
||||
|
||||
static void drive_info_del(DriveInfo *dinfo);
|
||||
|
||||
/* All the BlockBackends (except for hidden ones) */
|
||||
|
@ -91,6 +101,40 @@ BlockBackend *blk_new_with_bs(const char *name, Error **errp)
|
|||
return blk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calls blk_new_with_bs() and then calls bdrv_open() on the BlockDriverState.
|
||||
*
|
||||
* Just as with bdrv_open(), after having called this function the reference to
|
||||
* @options belongs to the block layer (even on failure).
|
||||
*
|
||||
* TODO: Remove @filename and @flags; it should be possible to specify a whole
|
||||
* BDS tree just by specifying the @options QDict (or @reference,
|
||||
* alternatively). At the time of adding this function, this is not possible,
|
||||
* though, so callers of this function have to be able to specify @filename and
|
||||
* @flags.
|
||||
*/
|
||||
BlockBackend *blk_new_open(const char *name, const char *filename,
|
||||
const char *reference, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
int ret;
|
||||
|
||||
blk = blk_new_with_bs(name, errp);
|
||||
if (!blk) {
|
||||
QDECREF(options);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = bdrv_open(&blk->bs, filename, reference, options, flags, NULL, errp);
|
||||
if (ret < 0) {
|
||||
blk_unref(blk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
static void blk_delete(BlockBackend *blk)
|
||||
{
|
||||
assert(!blk->refcnt);
|
||||
|
@ -394,39 +438,137 @@ void blk_iostatus_enable(BlockBackend *blk)
|
|||
bdrv_iostatus_enable(blk->bs);
|
||||
}
|
||||
|
||||
static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
|
||||
size_t size)
|
||||
{
|
||||
int64_t len;
|
||||
|
||||
if (size > INT_MAX) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!blk_is_inserted(blk)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
len = blk_getlength(blk);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (offset > len || len - offset < size) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blk_check_request(BlockBackend *blk, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
if (sector_num < 0 || sector_num > INT64_MAX / BDRV_SECTOR_SIZE) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (nb_sectors < 0 || nb_sectors > INT_MAX / BDRV_SECTOR_SIZE) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return blk_check_byte_request(blk, sector_num * BDRV_SECTOR_SIZE,
|
||||
nb_sectors * BDRV_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf,
|
||||
int nb_sectors)
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_read(blk->bs, sector_num, buf, nb_sectors);
|
||||
}
|
||||
|
||||
int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf,
|
||||
int nb_sectors)
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_read_unthrottled(blk->bs, sector_num, buf, nb_sectors);
|
||||
}
|
||||
|
||||
int blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf,
|
||||
int nb_sectors)
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_write(blk->bs, sector_num, buf, nb_sectors);
|
||||
}
|
||||
|
||||
static void error_callback_bh(void *opaque)
|
||||
{
|
||||
struct BlockBackendAIOCB *acb = opaque;
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->common.cb(acb->common.opaque, acb->ret);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
static BlockAIOCB *abort_aio_request(BlockBackend *blk, BlockCompletionFunc *cb,
|
||||
void *opaque, int ret)
|
||||
{
|
||||
struct BlockBackendAIOCB *acb;
|
||||
QEMUBH *bh;
|
||||
|
||||
acb = blk_aio_get(&block_backend_aiocb_info, blk, cb, opaque);
|
||||
acb->ret = ret;
|
||||
|
||||
bh = aio_bh_new(blk_get_aio_context(blk), error_callback_bh, acb);
|
||||
acb->bh = bh;
|
||||
qemu_bh_schedule(bh);
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_aio_write_zeroes(BlockBackend *blk, int64_t sector_num,
|
||||
int nb_sectors, BdrvRequestFlags flags,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return abort_aio_request(blk, cb, opaque, ret);
|
||||
}
|
||||
|
||||
return bdrv_aio_write_zeroes(blk->bs, sector_num, nb_sectors, flags,
|
||||
cb, opaque);
|
||||
}
|
||||
|
||||
int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count)
|
||||
{
|
||||
int ret = blk_check_byte_request(blk, offset, count);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_pread(blk->bs, offset, buf, count);
|
||||
}
|
||||
|
||||
int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count)
|
||||
{
|
||||
int ret = blk_check_byte_request(blk, offset, count);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_pwrite(blk->bs, offset, buf, count);
|
||||
}
|
||||
|
||||
|
@ -440,10 +582,20 @@ void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr)
|
|||
bdrv_get_geometry(blk->bs, nb_sectors_ptr);
|
||||
}
|
||||
|
||||
int64_t blk_nb_sectors(BlockBackend *blk)
|
||||
{
|
||||
return bdrv_nb_sectors(blk->bs);
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num,
|
||||
QEMUIOVector *iov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return abort_aio_request(blk, cb, opaque, ret);
|
||||
}
|
||||
|
||||
return bdrv_aio_readv(blk->bs, sector_num, iov, nb_sectors, cb, opaque);
|
||||
}
|
||||
|
||||
|
@ -451,6 +603,11 @@ BlockAIOCB *blk_aio_writev(BlockBackend *blk, int64_t sector_num,
|
|||
QEMUIOVector *iov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return abort_aio_request(blk, cb, opaque, ret);
|
||||
}
|
||||
|
||||
return bdrv_aio_writev(blk->bs, sector_num, iov, nb_sectors, cb, opaque);
|
||||
}
|
||||
|
||||
|
@ -464,6 +621,11 @@ BlockAIOCB *blk_aio_discard(BlockBackend *blk,
|
|||
int64_t sector_num, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return abort_aio_request(blk, cb, opaque, ret);
|
||||
}
|
||||
|
||||
return bdrv_aio_discard(blk->bs, sector_num, nb_sectors, cb, opaque);
|
||||
}
|
||||
|
||||
|
@ -479,6 +641,15 @@ void blk_aio_cancel_async(BlockAIOCB *acb)
|
|||
|
||||
int blk_aio_multiwrite(BlockBackend *blk, BlockRequest *reqs, int num_reqs)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < num_reqs; i++) {
|
||||
ret = blk_check_request(blk, reqs[i].sector, reqs[i].nb_sectors);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return bdrv_aio_multiwrite(blk->bs, reqs, num_reqs);
|
||||
}
|
||||
|
||||
|
@ -495,6 +666,11 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
|
|||
|
||||
int blk_co_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors)
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_co_discard(blk->bs, sector_num, nb_sectors);
|
||||
}
|
||||
|
||||
|
@ -668,3 +844,51 @@ void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk,
|
|||
{
|
||||
return qemu_aio_get(aiocb_info, blk_bs(blk), cb, opaque);
|
||||
}
|
||||
|
||||
int coroutine_fn blk_co_write_zeroes(BlockBackend *blk, int64_t sector_num,
|
||||
int nb_sectors, BdrvRequestFlags flags)
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_co_write_zeroes(blk->bs, sector_num, nb_sectors, flags);
|
||||
}
|
||||
|
||||
int blk_write_compressed(BlockBackend *blk, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_write_compressed(blk->bs, sector_num, buf, nb_sectors);
|
||||
}
|
||||
|
||||
int blk_truncate(BlockBackend *blk, int64_t offset)
|
||||
{
|
||||
return bdrv_truncate(blk->bs, offset);
|
||||
}
|
||||
|
||||
int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors)
|
||||
{
|
||||
int ret = blk_check_request(blk, sector_num, nb_sectors);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_discard(blk->bs, sector_num, nb_sectors);
|
||||
}
|
||||
|
||||
int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,
|
||||
int64_t pos, int size)
|
||||
{
|
||||
return bdrv_save_vmstate(blk->bs, buf, pos, size);
|
||||
}
|
||||
|
||||
int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size)
|
||||
{
|
||||
return bdrv_load_vmstate(blk->bs, buf, pos, size);
|
||||
}
|
||||
|
|
|
@ -43,20 +43,23 @@ static void nbd_recv_coroutines_enter_all(NbdClientSession *s)
|
|||
}
|
||||
}
|
||||
|
||||
static void nbd_teardown_connection(NbdClientSession *client)
|
||||
static void nbd_teardown_connection(BlockDriverState *bs)
|
||||
{
|
||||
NbdClientSession *client = nbd_get_client_session(bs);
|
||||
|
||||
/* finish any pending coroutines */
|
||||
shutdown(client->sock, 2);
|
||||
nbd_recv_coroutines_enter_all(client);
|
||||
|
||||
nbd_client_session_detach_aio_context(client);
|
||||
nbd_client_detach_aio_context(bs);
|
||||
closesocket(client->sock);
|
||||
client->sock = -1;
|
||||
}
|
||||
|
||||
static void nbd_reply_ready(void *opaque)
|
||||
{
|
||||
NbdClientSession *s = opaque;
|
||||
BlockDriverState *bs = opaque;
|
||||
NbdClientSession *s = nbd_get_client_session(bs);
|
||||
uint64_t i;
|
||||
int ret;
|
||||
|
||||
|
@ -89,28 +92,40 @@ static void nbd_reply_ready(void *opaque)
|
|||
}
|
||||
|
||||
fail:
|
||||
nbd_teardown_connection(s);
|
||||
nbd_teardown_connection(bs);
|
||||
}
|
||||
|
||||
static void nbd_restart_write(void *opaque)
|
||||
{
|
||||
NbdClientSession *s = opaque;
|
||||
BlockDriverState *bs = opaque;
|
||||
|
||||
qemu_coroutine_enter(s->send_coroutine, NULL);
|
||||
qemu_coroutine_enter(nbd_get_client_session(bs)->send_coroutine, NULL);
|
||||
}
|
||||
|
||||
static int nbd_co_send_request(NbdClientSession *s,
|
||||
struct nbd_request *request,
|
||||
QEMUIOVector *qiov, int offset)
|
||||
static int nbd_co_send_request(BlockDriverState *bs,
|
||||
struct nbd_request *request,
|
||||
QEMUIOVector *qiov, int offset)
|
||||
{
|
||||
NbdClientSession *s = nbd_get_client_session(bs);
|
||||
AioContext *aio_context;
|
||||
int rc, ret;
|
||||
int rc, ret, i;
|
||||
|
||||
qemu_co_mutex_lock(&s->send_mutex);
|
||||
|
||||
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
||||
if (s->recv_coroutine[i] == NULL) {
|
||||
s->recv_coroutine[i] = qemu_coroutine_self();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(i < MAX_NBD_REQUESTS);
|
||||
request->handle = INDEX_TO_HANDLE(s, i);
|
||||
s->send_coroutine = qemu_coroutine_self();
|
||||
aio_context = bdrv_get_aio_context(s->bs);
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_set_fd_handler(aio_context, s->sock,
|
||||
nbd_reply_ready, nbd_restart_write, s);
|
||||
nbd_reply_ready, nbd_restart_write, bs);
|
||||
if (qiov) {
|
||||
if (!s->is_unix) {
|
||||
socket_set_cork(s->sock, 1);
|
||||
|
@ -129,7 +144,7 @@ static int nbd_co_send_request(NbdClientSession *s,
|
|||
} else {
|
||||
rc = nbd_send_request(s->sock, request);
|
||||
}
|
||||
aio_set_fd_handler(aio_context, s->sock, nbd_reply_ready, NULL, s);
|
||||
aio_set_fd_handler(aio_context, s->sock, nbd_reply_ready, NULL, bs);
|
||||
s->send_coroutine = NULL;
|
||||
qemu_co_mutex_unlock(&s->send_mutex);
|
||||
return rc;
|
||||
|
@ -164,8 +179,6 @@ static void nbd_co_receive_reply(NbdClientSession *s,
|
|||
static void nbd_coroutine_start(NbdClientSession *s,
|
||||
struct nbd_request *request)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Poor man semaphore. The free_sema is locked when no other request
|
||||
* can be accepted, and unlocked after receiving one reply. */
|
||||
if (s->in_flight >= MAX_NBD_REQUESTS - 1) {
|
||||
|
@ -174,15 +187,7 @@ static void nbd_coroutine_start(NbdClientSession *s,
|
|||
}
|
||||
s->in_flight++;
|
||||
|
||||
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
||||
if (s->recv_coroutine[i] == NULL) {
|
||||
s->recv_coroutine[i] = qemu_coroutine_self();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(i < MAX_NBD_REQUESTS);
|
||||
request->handle = INDEX_TO_HANDLE(s, i);
|
||||
/* s->recv_coroutine[i] is set as soon as we get the send_lock. */
|
||||
}
|
||||
|
||||
static void nbd_coroutine_end(NbdClientSession *s,
|
||||
|
@ -195,10 +200,11 @@ static void nbd_coroutine_end(NbdClientSession *s,
|
|||
}
|
||||
}
|
||||
|
||||
static int nbd_co_readv_1(NbdClientSession *client, int64_t sector_num,
|
||||
static int nbd_co_readv_1(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int offset)
|
||||
{
|
||||
NbdClientSession *client = nbd_get_client_session(bs);
|
||||
struct nbd_request request = { .type = NBD_CMD_READ };
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
@ -207,7 +213,7 @@ static int nbd_co_readv_1(NbdClientSession *client, int64_t sector_num,
|
|||
request.len = nb_sectors * 512;
|
||||
|
||||
nbd_coroutine_start(client, &request);
|
||||
ret = nbd_co_send_request(client, &request, NULL, 0);
|
||||
ret = nbd_co_send_request(bs, &request, NULL, 0);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
|
@ -218,15 +224,16 @@ static int nbd_co_readv_1(NbdClientSession *client, int64_t sector_num,
|
|||
|
||||
}
|
||||
|
||||
static int nbd_co_writev_1(NbdClientSession *client, int64_t sector_num,
|
||||
static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int offset)
|
||||
{
|
||||
NbdClientSession *client = nbd_get_client_session(bs);
|
||||
struct nbd_request request = { .type = NBD_CMD_WRITE };
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
||||
if (!bdrv_enable_write_cache(client->bs) &&
|
||||
if (!bdrv_enable_write_cache(bs) &&
|
||||
(client->nbdflags & NBD_FLAG_SEND_FUA)) {
|
||||
request.type |= NBD_CMD_FLAG_FUA;
|
||||
}
|
||||
|
@ -235,7 +242,7 @@ static int nbd_co_writev_1(NbdClientSession *client, int64_t sector_num,
|
|||
request.len = nb_sectors * 512;
|
||||
|
||||
nbd_coroutine_start(client, &request);
|
||||
ret = nbd_co_send_request(client, &request, qiov, offset);
|
||||
ret = nbd_co_send_request(bs, &request, qiov, offset);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
|
@ -249,14 +256,13 @@ static int nbd_co_writev_1(NbdClientSession *client, int64_t sector_num,
|
|||
* remain aligned to 4K. */
|
||||
#define NBD_MAX_SECTORS 2040
|
||||
|
||||
int nbd_client_session_co_readv(NbdClientSession *client, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
int nbd_client_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
int offset = 0;
|
||||
int ret;
|
||||
while (nb_sectors > NBD_MAX_SECTORS) {
|
||||
ret = nbd_co_readv_1(client, sector_num,
|
||||
NBD_MAX_SECTORS, qiov, offset);
|
||||
ret = nbd_co_readv_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -264,17 +270,16 @@ int nbd_client_session_co_readv(NbdClientSession *client, int64_t sector_num,
|
|||
sector_num += NBD_MAX_SECTORS;
|
||||
nb_sectors -= NBD_MAX_SECTORS;
|
||||
}
|
||||
return nbd_co_readv_1(client, sector_num, nb_sectors, qiov, offset);
|
||||
return nbd_co_readv_1(bs, sector_num, nb_sectors, qiov, offset);
|
||||
}
|
||||
|
||||
int nbd_client_session_co_writev(NbdClientSession *client, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
int nbd_client_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
int offset = 0;
|
||||
int ret;
|
||||
while (nb_sectors > NBD_MAX_SECTORS) {
|
||||
ret = nbd_co_writev_1(client, sector_num,
|
||||
NBD_MAX_SECTORS, qiov, offset);
|
||||
ret = nbd_co_writev_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -282,11 +287,12 @@ int nbd_client_session_co_writev(NbdClientSession *client, int64_t sector_num,
|
|||
sector_num += NBD_MAX_SECTORS;
|
||||
nb_sectors -= NBD_MAX_SECTORS;
|
||||
}
|
||||
return nbd_co_writev_1(client, sector_num, nb_sectors, qiov, offset);
|
||||
return nbd_co_writev_1(bs, sector_num, nb_sectors, qiov, offset);
|
||||
}
|
||||
|
||||
int nbd_client_session_co_flush(NbdClientSession *client)
|
||||
int nbd_client_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
NbdClientSession *client = nbd_get_client_session(bs);
|
||||
struct nbd_request request = { .type = NBD_CMD_FLUSH };
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
@ -303,7 +309,7 @@ int nbd_client_session_co_flush(NbdClientSession *client)
|
|||
request.len = 0;
|
||||
|
||||
nbd_coroutine_start(client, &request);
|
||||
ret = nbd_co_send_request(client, &request, NULL, 0);
|
||||
ret = nbd_co_send_request(bs, &request, NULL, 0);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
|
@ -313,9 +319,10 @@ int nbd_client_session_co_flush(NbdClientSession *client)
|
|||
return -reply.error;
|
||||
}
|
||||
|
||||
int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
NbdClientSession *client = nbd_get_client_session(bs);
|
||||
struct nbd_request request = { .type = NBD_CMD_TRIM };
|
||||
struct nbd_reply reply;
|
||||
ssize_t ret;
|
||||
|
@ -327,7 +334,7 @@ int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num,
|
|||
request.len = nb_sectors * 512;
|
||||
|
||||
nbd_coroutine_start(client, &request);
|
||||
ret = nbd_co_send_request(client, &request, NULL, 0);
|
||||
ret = nbd_co_send_request(bs, &request, NULL, 0);
|
||||
if (ret < 0) {
|
||||
reply.error = -ret;
|
||||
} else {
|
||||
|
@ -338,43 +345,41 @@ int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num,
|
|||
|
||||
}
|
||||
|
||||
void nbd_client_session_detach_aio_context(NbdClientSession *client)
|
||||
void nbd_client_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
aio_set_fd_handler(bdrv_get_aio_context(client->bs), client->sock,
|
||||
NULL, NULL, NULL);
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs),
|
||||
nbd_get_client_session(bs)->sock, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void nbd_client_session_attach_aio_context(NbdClientSession *client,
|
||||
AioContext *new_context)
|
||||
void nbd_client_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
aio_set_fd_handler(new_context, client->sock,
|
||||
nbd_reply_ready, NULL, client);
|
||||
aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sock,
|
||||
nbd_reply_ready, NULL, bs);
|
||||
}
|
||||
|
||||
void nbd_client_session_close(NbdClientSession *client)
|
||||
void nbd_client_close(BlockDriverState *bs)
|
||||
{
|
||||
NbdClientSession *client = nbd_get_client_session(bs);
|
||||
struct nbd_request request = {
|
||||
.type = NBD_CMD_DISC,
|
||||
.from = 0,
|
||||
.len = 0
|
||||
};
|
||||
|
||||
if (!client->bs) {
|
||||
return;
|
||||
}
|
||||
if (client->sock == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
nbd_send_request(client->sock, &request);
|
||||
|
||||
nbd_teardown_connection(client);
|
||||
client->bs = NULL;
|
||||
nbd_teardown_connection(bs);
|
||||
}
|
||||
|
||||
int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs,
|
||||
int sock, const char *export, Error **errp)
|
||||
int nbd_client_init(BlockDriverState *bs, int sock, const char *export,
|
||||
Error **errp)
|
||||
{
|
||||
NbdClientSession *client = nbd_get_client_session(bs);
|
||||
int ret;
|
||||
|
||||
/* NBD handshake */
|
||||
|
@ -391,13 +396,12 @@ int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs,
|
|||
|
||||
qemu_co_mutex_init(&client->send_mutex);
|
||||
qemu_co_mutex_init(&client->free_sema);
|
||||
client->bs = bs;
|
||||
client->sock = sock;
|
||||
|
||||
/* Now that we're connected, set the socket to be non-blocking and
|
||||
* kick the reply mechanism. */
|
||||
qemu_set_nonblock(sock);
|
||||
nbd_client_session_attach_aio_context(client, bdrv_get_aio_context(bs));
|
||||
nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs));
|
||||
|
||||
logout("Established connection with NBD server\n");
|
||||
return 0;
|
||||
|
|
|
@ -31,24 +31,24 @@ typedef struct NbdClientSession {
|
|||
struct nbd_reply reply;
|
||||
|
||||
bool is_unix;
|
||||
|
||||
BlockDriverState *bs;
|
||||
} NbdClientSession;
|
||||
|
||||
int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs,
|
||||
int sock, const char *export_name, Error **errp);
|
||||
void nbd_client_session_close(NbdClientSession *client);
|
||||
NbdClientSession *nbd_get_client_session(BlockDriverState *bs);
|
||||
|
||||
int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num,
|
||||
int nb_sectors);
|
||||
int nbd_client_session_co_flush(NbdClientSession *client);
|
||||
int nbd_client_session_co_writev(NbdClientSession *client, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov);
|
||||
int nbd_client_session_co_readv(NbdClientSession *client, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov);
|
||||
int nbd_client_init(BlockDriverState *bs, int sock, const char *export_name,
|
||||
Error **errp);
|
||||
void nbd_client_close(BlockDriverState *bs);
|
||||
|
||||
void nbd_client_session_detach_aio_context(NbdClientSession *client);
|
||||
void nbd_client_session_attach_aio_context(NbdClientSession *client,
|
||||
AioContext *new_context);
|
||||
int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors);
|
||||
int nbd_client_co_flush(BlockDriverState *bs);
|
||||
int nbd_client_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov);
|
||||
int nbd_client_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov);
|
||||
|
||||
void nbd_client_detach_aio_context(BlockDriverState *bs);
|
||||
void nbd_client_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context);
|
||||
|
||||
#endif /* NBD_CLIENT_H */
|
||||
|
|
37
block/nbd.c
37
block/nbd.c
|
@ -224,6 +224,12 @@ static void nbd_config(BDRVNBDState *s, QDict *options, char **export,
|
|||
}
|
||||
}
|
||||
|
||||
NbdClientSession *nbd_get_client_session(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
return &s->client;
|
||||
}
|
||||
|
||||
static int nbd_establish_connection(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
@ -271,7 +277,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
}
|
||||
|
||||
/* NBD handshake */
|
||||
result = nbd_client_session_init(&s->client, bs, sock, export, errp);
|
||||
result = nbd_client_init(bs, sock, export, errp);
|
||||
g_free(export);
|
||||
return result;
|
||||
}
|
||||
|
@ -279,26 +285,18 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
return nbd_client_session_co_readv(&s->client, sector_num,
|
||||
nb_sectors, qiov);
|
||||
return nbd_client_co_readv(bs, sector_num, nb_sectors, qiov);
|
||||
}
|
||||
|
||||
static int nbd_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
return nbd_client_session_co_writev(&s->client, sector_num,
|
||||
nb_sectors, qiov);
|
||||
return nbd_client_co_writev(bs, sector_num, nb_sectors, qiov);
|
||||
}
|
||||
|
||||
static int nbd_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
return nbd_client_session_co_flush(&s->client);
|
||||
return nbd_client_co_flush(bs);
|
||||
}
|
||||
|
||||
static void nbd_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
|
@ -310,10 +308,7 @@ static void nbd_refresh_limits(BlockDriverState *bs, Error **errp)
|
|||
static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
return nbd_client_session_co_discard(&s->client, sector_num,
|
||||
nb_sectors);
|
||||
return nbd_client_co_discard(bs, sector_num, nb_sectors);
|
||||
}
|
||||
|
||||
static void nbd_close(BlockDriverState *bs)
|
||||
|
@ -321,7 +316,7 @@ static void nbd_close(BlockDriverState *bs)
|
|||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
qemu_opts_del(s->socket_opts);
|
||||
nbd_client_session_close(&s->client);
|
||||
nbd_client_close(bs);
|
||||
}
|
||||
|
||||
static int64_t nbd_getlength(BlockDriverState *bs)
|
||||
|
@ -333,17 +328,13 @@ static int64_t nbd_getlength(BlockDriverState *bs)
|
|||
|
||||
static void nbd_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
nbd_client_session_detach_aio_context(&s->client);
|
||||
nbd_client_detach_aio_context(bs);
|
||||
}
|
||||
|
||||
static void nbd_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
nbd_client_session_attach_aio_context(&s->client, new_context);
|
||||
nbd_client_attach_aio_context(bs, new_context);
|
||||
}
|
||||
|
||||
static void nbd_refresh_filename(BlockDriverState *bs)
|
||||
|
|
|
@ -2521,15 +2521,12 @@ static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
|
|||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t total_sectors = bs->total_sectors;
|
||||
int growable = bs->growable;
|
||||
bool zero_beyond_eof = bs->zero_beyond_eof;
|
||||
int ret;
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE);
|
||||
bs->growable = 1;
|
||||
bs->zero_beyond_eof = false;
|
||||
ret = bdrv_pwritev(bs, qcow2_vm_state_offset(s) + pos, qiov);
|
||||
bs->growable = growable;
|
||||
bs->zero_beyond_eof = zero_beyond_eof;
|
||||
|
||||
/* bdrv_co_do_writev will have increased the total_sectors value to include
|
||||
|
@ -2544,15 +2541,12 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
|||
int64_t pos, int size)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int growable = bs->growable;
|
||||
bool zero_beyond_eof = bs->zero_beyond_eof;
|
||||
int ret;
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_LOAD);
|
||||
bs->growable = 1;
|
||||
bs->zero_beyond_eof = false;
|
||||
ret = bdrv_pread(bs, qcow2_vm_state_offset(s) + pos, buf, size);
|
||||
bs->growable = growable;
|
||||
bs->zero_beyond_eof = zero_beyond_eof;
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -1047,7 +1047,7 @@ static int aio_worker(void *arg)
|
|||
switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) {
|
||||
case QEMU_AIO_READ:
|
||||
ret = handle_aiocb_rw(aiocb);
|
||||
if (ret >= 0 && ret < aiocb->aio_nbytes && aiocb->bs->growable) {
|
||||
if (ret >= 0 && ret < aiocb->aio_nbytes) {
|
||||
iov_memset(aiocb->aio_iov, aiocb->aio_niov, ret,
|
||||
0, aiocb->aio_nbytes - ret);
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ static int aio_worker(void *arg)
|
|||
switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) {
|
||||
case QEMU_AIO_READ:
|
||||
count = handle_aiocb_rw(aiocb);
|
||||
if (count < aiocb->aio_nbytes && aiocb->bs->growable) {
|
||||
if (count < aiocb->aio_nbytes) {
|
||||
/* A short read means that we have reached EOF. Pad the buffer
|
||||
* with zeros for bytes after EOF. */
|
||||
iov_memset(aiocb->aio_iov, aiocb->aio_niov, count,
|
||||
|
|
|
@ -1730,7 +1730,7 @@ static int sd_create(const char *filename, QemuOpts *opts,
|
|||
BlockDriver *drv;
|
||||
|
||||
/* Currently, only Sheepdog backing image is supported. */
|
||||
drv = bdrv_find_protocol(backing_file, true);
|
||||
drv = bdrv_find_protocol(backing_file, true, NULL);
|
||||
if (!drv || strcmp(drv->protocol_name, "sheepdog") != 0) {
|
||||
error_setg(errp, "backing_file must be a sheepdog image");
|
||||
ret = -EINVAL;
|
||||
|
@ -2117,7 +2117,7 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
|
|||
int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE;
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
|
||||
if (bs->growable && offset > s->inode.vdi_size) {
|
||||
if (offset > s->inode.vdi_size) {
|
||||
ret = sd_truncate(bs, offset);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
|
|
|
@ -843,8 +843,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
|||
}
|
||||
|
||||
extent_path = g_malloc0(PATH_MAX);
|
||||
path_combine(extent_path, sizeof(extent_path),
|
||||
desc_file_path, fname);
|
||||
path_combine(extent_path, PATH_MAX, desc_file_path, fname);
|
||||
extent_file = NULL;
|
||||
ret = bdrv_open(&extent_file, extent_path, NULL, NULL,
|
||||
bs->open_flags | BDRV_O_PROTOCOL, NULL, errp);
|
||||
|
|
94
blockdev.c
94
blockdev.c
|
@ -354,13 +354,11 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
|||
ThrottleConfig cfg;
|
||||
int snapshot = 0;
|
||||
bool copy_on_read;
|
||||
int ret;
|
||||
Error *error = NULL;
|
||||
QemuOpts *opts;
|
||||
const char *id;
|
||||
bool has_driver_specific_opts;
|
||||
BlockdevDetectZeroesOptions detect_zeroes;
|
||||
BlockDriver *drv = NULL;
|
||||
|
||||
/* Check common options by copying from bs_opts to opts, all other options
|
||||
* stay in bs_opts for processing by bdrv_open(). */
|
||||
|
@ -426,11 +424,11 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
|||
goto early_err;
|
||||
}
|
||||
|
||||
drv = bdrv_find_format(buf);
|
||||
if (!drv) {
|
||||
error_setg(errp, "'%s' invalid format", buf);
|
||||
if (qdict_haskey(bs_opts, "driver")) {
|
||||
error_setg(errp, "Cannot specify both 'driver' and 'format'");
|
||||
goto early_err;
|
||||
}
|
||||
qdict_put(bs_opts, "driver", qstring_from_str(buf));
|
||||
}
|
||||
|
||||
/* disk I/O throttling */
|
||||
|
@ -505,13 +503,46 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
|||
}
|
||||
|
||||
/* init */
|
||||
blk = blk_new_with_bs(qemu_opts_id(opts), errp);
|
||||
if (!blk) {
|
||||
goto early_err;
|
||||
if ((!file || !*file) && !has_driver_specific_opts) {
|
||||
blk = blk_new_with_bs(qemu_opts_id(opts), errp);
|
||||
if (!blk) {
|
||||
goto early_err;
|
||||
}
|
||||
|
||||
bs = blk_bs(blk);
|
||||
bs->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
|
||||
bs->read_only = ro;
|
||||
|
||||
QDECREF(bs_opts);
|
||||
} else {
|
||||
if (file && !*file) {
|
||||
file = NULL;
|
||||
}
|
||||
|
||||
if (snapshot) {
|
||||
/* always use cache=unsafe with snapshot */
|
||||
bdrv_flags &= ~BDRV_O_CACHE_MASK;
|
||||
bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH);
|
||||
}
|
||||
|
||||
if (copy_on_read) {
|
||||
bdrv_flags |= BDRV_O_COPY_ON_READ;
|
||||
}
|
||||
|
||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||
bdrv_flags |= BDRV_O_INCOMING;
|
||||
}
|
||||
|
||||
bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
|
||||
|
||||
blk = blk_new_open(qemu_opts_id(opts), file, NULL, bs_opts, bdrv_flags,
|
||||
errp);
|
||||
if (!blk) {
|
||||
goto err_no_bs_opts;
|
||||
}
|
||||
bs = blk_bs(blk);
|
||||
}
|
||||
bs = blk_bs(blk);
|
||||
bs->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
|
||||
bs->read_only = ro;
|
||||
|
||||
bs->detect_zeroes = detect_zeroes;
|
||||
|
||||
bdrv_set_on_error(bs, on_read_error, on_write_error);
|
||||
|
@ -522,53 +553,14 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
|||
bdrv_set_io_limits(bs, &cfg);
|
||||
}
|
||||
|
||||
if (!file || !*file) {
|
||||
if (has_driver_specific_opts) {
|
||||
file = NULL;
|
||||
} else {
|
||||
QDECREF(bs_opts);
|
||||
qemu_opts_del(opts);
|
||||
return blk;
|
||||
}
|
||||
}
|
||||
if (snapshot) {
|
||||
/* always use cache=unsafe with snapshot */
|
||||
bdrv_flags &= ~BDRV_O_CACHE_MASK;
|
||||
bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH);
|
||||
}
|
||||
|
||||
if (copy_on_read) {
|
||||
bdrv_flags |= BDRV_O_COPY_ON_READ;
|
||||
}
|
||||
|
||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||
bdrv_flags |= BDRV_O_INCOMING;
|
||||
}
|
||||
|
||||
bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
|
||||
|
||||
QINCREF(bs_opts);
|
||||
ret = bdrv_open(&bs, file, NULL, bs_opts, bdrv_flags, drv, &error);
|
||||
assert(bs == blk_bs(blk));
|
||||
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "could not open disk image %s: %s",
|
||||
file ?: blk_name(blk), error_get_pretty(error));
|
||||
error_free(error);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (bdrv_key_required(bs)) {
|
||||
autostart = 0;
|
||||
}
|
||||
|
||||
QDECREF(bs_opts);
|
||||
err_no_bs_opts:
|
||||
qemu_opts_del(opts);
|
||||
|
||||
return blk;
|
||||
|
||||
err:
|
||||
blk_unref(blk);
|
||||
early_err:
|
||||
qemu_opts_del(opts);
|
||||
err_no_opts:
|
||||
|
|
4
cpus.c
4
cpus.c
|
@ -361,15 +361,19 @@ static void icount_warp_rt(void *opaque)
|
|||
void qtest_clock_warp(int64_t dest)
|
||||
{
|
||||
int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
AioContext *aio_context;
|
||||
assert(qtest_enabled());
|
||||
aio_context = qemu_get_aio_context();
|
||||
while (clock < dest) {
|
||||
int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
|
||||
int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
|
||||
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock);
|
||||
timers_state.qemu_icount_bias += warp;
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock);
|
||||
|
||||
qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
|
||||
timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
|
||||
clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
|
|
9
hmp.c
9
hmp.c
|
@ -16,6 +16,7 @@
|
|||
#include "hmp.h"
|
||||
#include "net/net.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qmp-commands.h"
|
||||
|
@ -1720,14 +1721,14 @@ void hmp_chardev_remove(Monitor *mon, const QDict *qdict)
|
|||
|
||||
void hmp_qemu_io(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *blk;
|
||||
const char* device = qdict_get_str(qdict, "device");
|
||||
const char* command = qdict_get_str(qdict, "command");
|
||||
Error *err = NULL;
|
||||
|
||||
bs = bdrv_find(device);
|
||||
if (bs) {
|
||||
qemuio_command(bs, command);
|
||||
blk = blk_by_name(device);
|
||||
if (blk) {
|
||||
qemuio_command(blk, command);
|
||||
} else {
|
||||
error_set(&err, QERR_DEVICE_NOT_FOUND, device);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
#include "qemu/iov.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/virtio/virtio-access.h"
|
||||
#include "hw/virtio/dataplane/vring.h"
|
||||
#include "hw/virtio/dataplane/vring-accessors.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "hw/virtio/virtio-blk.h"
|
||||
#include "virtio-blk.h"
|
||||
|
@ -75,7 +77,7 @@ static void complete_request_vring(VirtIOBlockReq *req, unsigned char status)
|
|||
VirtIOBlockDataPlane *s = req->dev->dataplane;
|
||||
stb_p(&req->in->status, status);
|
||||
|
||||
vring_push(&req->dev->dataplane->vring, &req->elem,
|
||||
vring_push(s->vdev, &req->dev->dataplane->vring, &req->elem,
|
||||
req->qiov.size + sizeof(*req->in));
|
||||
|
||||
/* Suppress notification to guest by BH and its scheduled
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
#include "xen_blkif.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
|
@ -897,30 +899,23 @@ static int blk_connect(struct XenDevice *xendev)
|
|||
blkdev->dinfo = drive_get(IF_XEN, 0, index);
|
||||
if (!blkdev->dinfo) {
|
||||
Error *local_err = NULL;
|
||||
BlockBackend *blk;
|
||||
BlockDriver *drv;
|
||||
BlockDriverState *bs;
|
||||
QDict *options = NULL;
|
||||
|
||||
if (strcmp(blkdev->fileproto, "<unset>")) {
|
||||
options = qdict_new();
|
||||
qdict_put(options, "driver", qstring_from_str(blkdev->fileproto));
|
||||
}
|
||||
|
||||
/* setup via xenbus -> create new block driver instance */
|
||||
xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
|
||||
blk = blk_new_with_bs(blkdev->dev, NULL);
|
||||
if (!blk) {
|
||||
return -1;
|
||||
}
|
||||
blkdev->blk = blk;
|
||||
|
||||
bs = blk_bs(blk);
|
||||
drv = bdrv_find_whitelisted_format(blkdev->fileproto, readonly);
|
||||
if (bdrv_open(&bs, blkdev->filename, NULL, NULL, qflags,
|
||||
drv, &local_err) != 0) {
|
||||
blkdev->blk = blk_new_open(blkdev->dev, blkdev->filename, NULL, options,
|
||||
qflags, &local_err);
|
||||
if (!blkdev->blk) {
|
||||
xen_be_printf(&blkdev->xendev, 0, "error: %s\n",
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
blk_unref(blk);
|
||||
blkdev->blk = NULL;
|
||||
return -1;
|
||||
}
|
||||
assert(bs == blk_bs(blk));
|
||||
} else {
|
||||
/* setup via qemu cmdline -> already setup for us */
|
||||
xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n");
|
||||
|
|
|
@ -94,7 +94,7 @@ void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req)
|
|||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(req->vring->parent);
|
||||
|
||||
vring_push(&req->vring->vring, &req->elem,
|
||||
vring_push(vdev, &req->vring->vring, &req->elem,
|
||||
req->qsgl.size + req->resp_iov.size);
|
||||
|
||||
if (vring_should_notify(vdev, &req->vring->vring)) {
|
||||
|
|
|
@ -2,7 +2,7 @@ common-obj-y += virtio-rng.o
|
|||
common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
|
||||
common-obj-y += virtio-bus.o
|
||||
common-obj-y += virtio-mmio.o
|
||||
common-obj-$(CONFIG_VIRTIO) += dataplane/
|
||||
obj-$(CONFIG_VIRTIO) += dataplane/
|
||||
|
||||
obj-y += virtio.o virtio-balloon.o
|
||||
obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o
|
||||
|
|
|
@ -1 +1 @@
|
|||
common-obj-y += vring.o
|
||||
obj-y += vring.o
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
#include "hw/hw.h"
|
||||
#include "exec/memory.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/virtio/virtio-access.h"
|
||||
#include "hw/virtio/dataplane/vring.h"
|
||||
#include "hw/virtio/dataplane/vring-accessors.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
/* vring_map can be coupled with vring_unmap or (if you still have the
|
||||
|
@ -83,7 +85,7 @@ bool vring_setup(Vring *vring, VirtIODevice *vdev, int n)
|
|||
vring_init(&vring->vr, virtio_queue_get_num(vdev, n), vring_ptr, 4096);
|
||||
|
||||
vring->last_avail_idx = virtio_queue_get_last_avail_idx(vdev, n);
|
||||
vring->last_used_idx = vring->vr.used->idx;
|
||||
vring->last_used_idx = vring_get_used_idx(vdev, vring);
|
||||
vring->signalled_used = 0;
|
||||
vring->signalled_used_valid = false;
|
||||
|
||||
|
@ -104,7 +106,7 @@ void vring_teardown(Vring *vring, VirtIODevice *vdev, int n)
|
|||
void vring_disable_notification(VirtIODevice *vdev, Vring *vring)
|
||||
{
|
||||
if (!(vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX))) {
|
||||
vring->vr.used->flags |= VRING_USED_F_NO_NOTIFY;
|
||||
vring_set_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,10 +119,10 @@ bool vring_enable_notification(VirtIODevice *vdev, Vring *vring)
|
|||
if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) {
|
||||
vring_avail_event(&vring->vr) = vring->vr.avail->idx;
|
||||
} else {
|
||||
vring->vr.used->flags &= ~VRING_USED_F_NO_NOTIFY;
|
||||
vring_clear_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY);
|
||||
}
|
||||
smp_mb(); /* ensure update is seen before reading avail_idx */
|
||||
return !vring_more_avail(vring);
|
||||
return !vring_more_avail(vdev, vring);
|
||||
}
|
||||
|
||||
/* This is stolen from linux/drivers/vhost/vhost.c:vhost_notify() */
|
||||
|
@ -134,12 +136,13 @@ bool vring_should_notify(VirtIODevice *vdev, Vring *vring)
|
|||
smp_mb();
|
||||
|
||||
if ((vdev->guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) &&
|
||||
unlikely(vring->vr.avail->idx == vring->last_avail_idx)) {
|
||||
unlikely(!vring_more_avail(vdev, vring))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX))) {
|
||||
return !(vring->vr.avail->flags & VRING_AVAIL_F_NO_INTERRUPT);
|
||||
return !(vring_get_avail_flags(vdev, vring) &
|
||||
VRING_AVAIL_F_NO_INTERRUPT);
|
||||
}
|
||||
old = vring->signalled_used;
|
||||
v = vring->signalled_used_valid;
|
||||
|
@ -202,9 +205,19 @@ static int get_desc(Vring *vring, VirtQueueElement *elem,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void copy_in_vring_desc(VirtIODevice *vdev,
|
||||
const struct vring_desc *guest,
|
||||
struct vring_desc *host)
|
||||
{
|
||||
host->addr = virtio_ldq_p(vdev, &guest->addr);
|
||||
host->len = virtio_ldl_p(vdev, &guest->len);
|
||||
host->flags = virtio_lduw_p(vdev, &guest->flags);
|
||||
host->next = virtio_lduw_p(vdev, &guest->next);
|
||||
}
|
||||
|
||||
/* This is stolen from linux/drivers/vhost/vhost.c. */
|
||||
static int get_indirect(Vring *vring, VirtQueueElement *elem,
|
||||
struct vring_desc *indirect)
|
||||
static int get_indirect(VirtIODevice *vdev, Vring *vring,
|
||||
VirtQueueElement *elem, struct vring_desc *indirect)
|
||||
{
|
||||
struct vring_desc desc;
|
||||
unsigned int i = 0, count, found = 0;
|
||||
|
@ -244,7 +257,7 @@ static int get_indirect(Vring *vring, VirtQueueElement *elem,
|
|||
vring->broken = true;
|
||||
return -EFAULT;
|
||||
}
|
||||
desc = *desc_ptr;
|
||||
copy_in_vring_desc(vdev, desc_ptr, &desc);
|
||||
memory_region_unref(mr);
|
||||
|
||||
/* Ensure descriptor has been loaded before accessing fields */
|
||||
|
@ -320,7 +333,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
|
|||
|
||||
/* Check it isn't doing very strange things with descriptor numbers. */
|
||||
last_avail_idx = vring->last_avail_idx;
|
||||
avail_idx = vring->vr.avail->idx;
|
||||
avail_idx = vring_get_avail_idx(vdev, vring);
|
||||
barrier(); /* load indices now and not again later */
|
||||
|
||||
if (unlikely((uint16_t)(avail_idx - last_avail_idx) > num)) {
|
||||
|
@ -341,7 +354,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
|
|||
|
||||
/* Grab the next descriptor number they're advertising, and increment
|
||||
* the index we've seen. */
|
||||
head = vring->vr.avail->ring[last_avail_idx % num];
|
||||
head = vring_get_avail_ring(vdev, vring, last_avail_idx % num);
|
||||
|
||||
elem->index = head;
|
||||
|
||||
|
@ -365,13 +378,13 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
|
|||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
desc = vring->vr.desc[i];
|
||||
copy_in_vring_desc(vdev, &vring->vr.desc[i], &desc);
|
||||
|
||||
/* Ensure descriptor is loaded before accessing fields */
|
||||
barrier();
|
||||
|
||||
if (desc.flags & VRING_DESC_F_INDIRECT) {
|
||||
ret = get_indirect(vring, elem, &desc);
|
||||
ret = get_indirect(vdev, vring, elem, &desc);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -407,9 +420,9 @@ out:
|
|||
*
|
||||
* Stolen from linux/drivers/vhost/vhost.c.
|
||||
*/
|
||||
void vring_push(Vring *vring, VirtQueueElement *elem, int len)
|
||||
void vring_push(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem,
|
||||
int len)
|
||||
{
|
||||
struct vring_used_elem *used;
|
||||
unsigned int head = elem->index;
|
||||
uint16_t new;
|
||||
|
||||
|
@ -422,14 +435,16 @@ void vring_push(Vring *vring, VirtQueueElement *elem, int len)
|
|||
|
||||
/* The virtqueue contains a ring of used buffers. Get a pointer to the
|
||||
* next entry in that used ring. */
|
||||
used = &vring->vr.used->ring[vring->last_used_idx % vring->vr.num];
|
||||
used->id = head;
|
||||
used->len = len;
|
||||
vring_set_used_ring_id(vdev, vring, vring->last_used_idx % vring->vr.num,
|
||||
head);
|
||||
vring_set_used_ring_len(vdev, vring, vring->last_used_idx % vring->vr.num,
|
||||
len);
|
||||
|
||||
/* Make sure buffer is written before we update index. */
|
||||
smp_wmb();
|
||||
|
||||
new = vring->vr.used->idx = ++vring->last_used_idx;
|
||||
new = ++vring->last_used_idx;
|
||||
vring_set_used_idx(vdev, vring, new);
|
||||
if (unlikely((int16_t)(new - vring->signalled_used) < (uint16_t)1)) {
|
||||
vring->signalled_used_valid = false;
|
||||
}
|
||||
|
|
|
@ -168,7 +168,8 @@ void bdrv_io_limits_disable(BlockDriverState *bs);
|
|||
void bdrv_init(void);
|
||||
void bdrv_init_with_whitelist(void);
|
||||
BlockDriver *bdrv_find_protocol(const char *filename,
|
||||
bool allow_protocol_prefix);
|
||||
bool allow_protocol_prefix,
|
||||
Error **errp);
|
||||
BlockDriver *bdrv_find_format(const char *format_name);
|
||||
BlockDriver *bdrv_find_whitelisted_format(const char *format_name,
|
||||
bool readonly);
|
||||
|
|
|
@ -369,9 +369,6 @@ struct BlockDriverState {
|
|||
/* I/O Limits */
|
||||
BlockLimits bl;
|
||||
|
||||
/* Whether the disk can expand beyond total_sectors */
|
||||
int growable;
|
||||
|
||||
/* Whether produces zeros when read beyond eof */
|
||||
bool zero_beyond_eof;
|
||||
|
||||
|
|
|
@ -99,7 +99,6 @@ void nbd_export_close_all(void);
|
|||
|
||||
NBDClient *nbd_client_new(NBDExport *exp, int csock,
|
||||
void (*close)(NBDClient *));
|
||||
void nbd_client_close(NBDClient *client);
|
||||
void nbd_client_get(NBDClient *client);
|
||||
void nbd_client_put(NBDClient *client);
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef VRING_ACCESSORS_H
|
||||
#define VRING_ACCESSORS_H
|
||||
|
||||
#include "hw/virtio/virtio_ring.h"
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "hw/virtio/virtio-access.h"
|
||||
|
||||
static inline uint16_t vring_get_used_idx(VirtIODevice *vdev, Vring *vring)
|
||||
{
|
||||
return virtio_tswap16(vdev, vring->vr.used->idx);
|
||||
}
|
||||
|
||||
static inline void vring_set_used_idx(VirtIODevice *vdev, Vring *vring,
|
||||
uint16_t idx)
|
||||
{
|
||||
vring->vr.used->idx = virtio_tswap16(vdev, idx);
|
||||
}
|
||||
|
||||
static inline uint16_t vring_get_avail_idx(VirtIODevice *vdev, Vring *vring)
|
||||
{
|
||||
return virtio_tswap16(vdev, vring->vr.avail->idx);
|
||||
}
|
||||
|
||||
static inline uint16_t vring_get_avail_ring(VirtIODevice *vdev, Vring *vring,
|
||||
int i)
|
||||
{
|
||||
return virtio_tswap16(vdev, vring->vr.avail->ring[i]);
|
||||
}
|
||||
|
||||
static inline void vring_set_used_ring_id(VirtIODevice *vdev, Vring *vring,
|
||||
int i, uint32_t id)
|
||||
{
|
||||
vring->vr.used->ring[i].id = virtio_tswap32(vdev, id);
|
||||
}
|
||||
|
||||
static inline void vring_set_used_ring_len(VirtIODevice *vdev, Vring *vring,
|
||||
int i, uint32_t len)
|
||||
{
|
||||
vring->vr.used->ring[i].len = virtio_tswap32(vdev, len);
|
||||
}
|
||||
|
||||
static inline uint16_t vring_get_used_flags(VirtIODevice *vdev, Vring *vring)
|
||||
{
|
||||
return virtio_tswap16(vdev, vring->vr.used->flags);
|
||||
}
|
||||
|
||||
static inline uint16_t vring_get_avail_flags(VirtIODevice *vdev, Vring *vring)
|
||||
{
|
||||
return virtio_tswap16(vdev, vring->vr.avail->flags);
|
||||
}
|
||||
|
||||
static inline void vring_set_used_flags(VirtIODevice *vdev, Vring *vring,
|
||||
uint16_t flags)
|
||||
{
|
||||
vring->vr.used->flags |= virtio_tswap16(vdev, flags);
|
||||
}
|
||||
|
||||
static inline void vring_clear_used_flags(VirtIODevice *vdev, Vring *vring,
|
||||
uint16_t flags)
|
||||
{
|
||||
vring->vr.used->flags &= virtio_tswap16(vdev, ~flags);
|
||||
}
|
||||
|
||||
static inline unsigned int vring_get_num(Vring *vring)
|
||||
{
|
||||
return vring->vr.num;
|
||||
}
|
||||
|
||||
/* Are there more descriptors available? */
|
||||
static inline bool vring_more_avail(VirtIODevice *vdev, Vring *vring)
|
||||
{
|
||||
return vring_get_avail_idx(vdev, vring) != vring->last_avail_idx;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -31,17 +31,6 @@ typedef struct {
|
|||
bool broken; /* was there a fatal error? */
|
||||
} Vring;
|
||||
|
||||
static inline unsigned int vring_get_num(Vring *vring)
|
||||
{
|
||||
return vring->vr.num;
|
||||
}
|
||||
|
||||
/* Are there more descriptors available? */
|
||||
static inline bool vring_more_avail(Vring *vring)
|
||||
{
|
||||
return vring->vr.avail->idx != vring->last_avail_idx;
|
||||
}
|
||||
|
||||
/* Fail future vring_pop() and vring_push() calls until reset */
|
||||
static inline void vring_set_broken(Vring *vring)
|
||||
{
|
||||
|
@ -54,6 +43,7 @@ void vring_disable_notification(VirtIODevice *vdev, Vring *vring);
|
|||
bool vring_enable_notification(VirtIODevice *vdev, Vring *vring);
|
||||
bool vring_should_notify(VirtIODevice *vdev, Vring *vring);
|
||||
int vring_pop(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem);
|
||||
void vring_push(Vring *vring, VirtQueueElement *elem, int len);
|
||||
void vring_push(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem,
|
||||
int len);
|
||||
|
||||
#endif /* VRING_H */
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */
|
||||
|
||||
typedef int (*cfunc_t)(BlockDriverState *bs, int argc, char **argv);
|
||||
typedef int (*cfunc_t)(BlockBackend *blk, int argc, char **argv);
|
||||
typedef void (*helpfunc_t)(void);
|
||||
|
||||
typedef struct cmdinfo {
|
||||
|
@ -40,7 +40,7 @@ typedef struct cmdinfo {
|
|||
|
||||
extern bool qemuio_misalign;
|
||||
|
||||
bool qemuio_command(BlockDriverState *bs, const char *cmd);
|
||||
bool qemuio_command(BlockBackend *blk, const char *cmd);
|
||||
|
||||
void qemuio_add_command(const cmdinfo_t *ci);
|
||||
int qemuio_command_usage(const cmdinfo_t *ci);
|
||||
|
|
|
@ -62,6 +62,9 @@ typedef struct BlockDevOps {
|
|||
|
||||
BlockBackend *blk_new(const char *name, Error **errp);
|
||||
BlockBackend *blk_new_with_bs(const char *name, Error **errp);
|
||||
BlockBackend *blk_new_open(const char *name, const char *filename,
|
||||
const char *reference, QDict *options, int flags,
|
||||
Error **errp);
|
||||
void blk_ref(BlockBackend *blk);
|
||||
void blk_unref(BlockBackend *blk);
|
||||
const char *blk_name(BlockBackend *blk);
|
||||
|
@ -91,6 +94,7 @@ int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count);
|
|||
int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count);
|
||||
int64_t blk_getlength(BlockBackend *blk);
|
||||
void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr);
|
||||
int64_t blk_nb_sectors(BlockBackend *blk);
|
||||
BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num,
|
||||
QEMUIOVector *iov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque);
|
||||
|
@ -151,5 +155,14 @@ BlockAcctStats *blk_get_stats(BlockBackend *blk);
|
|||
|
||||
void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk,
|
||||
BlockCompletionFunc *cb, void *opaque);
|
||||
int coroutine_fn blk_co_write_zeroes(BlockBackend *blk, int64_t sector_num,
|
||||
int nb_sectors, BdrvRequestFlags flags);
|
||||
int blk_write_compressed(BlockBackend *blk, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors);
|
||||
int blk_truncate(BlockBackend *blk, int64_t offset);
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
|
8
nbd.c
8
nbd.c
|
@ -874,7 +874,7 @@ void nbd_client_put(NBDClient *client)
|
|||
{
|
||||
if (--client->refcount == 0) {
|
||||
/* The last reference should be dropped by client->close,
|
||||
* which is called by nbd_client_close.
|
||||
* which is called by client_close.
|
||||
*/
|
||||
assert(client->closing);
|
||||
|
||||
|
@ -889,7 +889,7 @@ void nbd_client_put(NBDClient *client)
|
|||
}
|
||||
}
|
||||
|
||||
void nbd_client_close(NBDClient *client)
|
||||
static void client_close(NBDClient *client)
|
||||
{
|
||||
if (client->closing) {
|
||||
return;
|
||||
|
@ -1026,7 +1026,7 @@ void nbd_export_close(NBDExport *exp)
|
|||
|
||||
nbd_export_get(exp);
|
||||
QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) {
|
||||
nbd_client_close(client);
|
||||
client_close(client);
|
||||
}
|
||||
nbd_export_set_name(exp, NULL);
|
||||
nbd_export_put(exp);
|
||||
|
@ -1311,7 +1311,7 @@ done:
|
|||
|
||||
out:
|
||||
nbd_request_put(req);
|
||||
nbd_client_close(client);
|
||||
client_close(client);
|
||||
}
|
||||
|
||||
static void nbd_read(void *opaque)
|
||||
|
|
208
qemu-img.c
208
qemu-img.c
|
@ -261,6 +261,7 @@ static int print_block_option_help(const char *filename, const char *fmt)
|
|||
{
|
||||
BlockDriver *drv, *proto_drv;
|
||||
QemuOptsList *create_opts = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* Find driver and parse its options */
|
||||
drv = bdrv_find_format(fmt);
|
||||
|
@ -271,9 +272,10 @@ static int print_block_option_help(const char *filename, const char *fmt)
|
|||
|
||||
create_opts = qemu_opts_append(create_opts, drv->create_opts);
|
||||
if (filename) {
|
||||
proto_drv = bdrv_find_protocol(filename, true);
|
||||
proto_drv = bdrv_find_protocol(filename, true, &local_err);
|
||||
if (!proto_drv) {
|
||||
error_report("Unknown protocol '%s'", filename);
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
qemu_opts_free(create_opts);
|
||||
return 1;
|
||||
}
|
||||
|
@ -291,32 +293,24 @@ static BlockBackend *img_open(const char *id, const char *filename,
|
|||
{
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
BlockDriver *drv;
|
||||
char password[256];
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
blk = blk_new_with_bs(id, &error_abort);
|
||||
bs = blk_bs(blk);
|
||||
QDict *options = NULL;
|
||||
|
||||
if (fmt) {
|
||||
drv = bdrv_find_format(fmt);
|
||||
if (!drv) {
|
||||
error_report("Unknown file format '%s'", fmt);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
drv = NULL;
|
||||
options = qdict_new();
|
||||
qdict_put(options, "driver", qstring_from_str(fmt));
|
||||
}
|
||||
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, flags, drv, &local_err);
|
||||
if (ret < 0) {
|
||||
blk = blk_new_open(id, filename, NULL, options, flags, &local_err);
|
||||
if (!blk) {
|
||||
error_report("Could not open '%s': %s", filename,
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs = blk_bs(blk);
|
||||
if (bdrv_is_encrypted(bs) && require_io) {
|
||||
qprintf(quiet, "Disk image '%s' is encrypted.\n", filename);
|
||||
if (read_password(password, sizeof(password)) < 0) {
|
||||
|
@ -1016,19 +1010,19 @@ static int64_t sectors_to_process(int64_t total, int64_t from)
|
|||
* Returns 0 in case sectors are filled with 0, 1 if sectors contain non-zero
|
||||
* data and negative value on error.
|
||||
*
|
||||
* @param bs: Driver used for accessing file
|
||||
* @param blk: BlockBackend for the image
|
||||
* @param sect_num: Number of first sector to check
|
||||
* @param sect_count: Number of sectors to check
|
||||
* @param filename: Name of disk file we are checking (logging purpose)
|
||||
* @param buffer: Allocated buffer for storing read data
|
||||
* @param quiet: Flag for quiet mode
|
||||
*/
|
||||
static int check_empty_sectors(BlockDriverState *bs, int64_t sect_num,
|
||||
static int check_empty_sectors(BlockBackend *blk, int64_t sect_num,
|
||||
int sect_count, const char *filename,
|
||||
uint8_t *buffer, bool quiet)
|
||||
{
|
||||
int pnum, ret = 0;
|
||||
ret = bdrv_read(bs, sect_num, buffer, sect_count);
|
||||
ret = blk_read(blk, sect_num, buffer, sect_count);
|
||||
if (ret < 0) {
|
||||
error_report("Error while reading offset %" PRId64 " of %s: %s",
|
||||
sectors_to_bytes(sect_num), filename, strerror(-ret));
|
||||
|
@ -1138,16 +1132,16 @@ static int img_compare(int argc, char **argv)
|
|||
}
|
||||
bs2 = blk_bs(blk2);
|
||||
|
||||
buf1 = qemu_blockalign(bs1, IO_BUF_SIZE);
|
||||
buf2 = qemu_blockalign(bs2, IO_BUF_SIZE);
|
||||
total_sectors1 = bdrv_nb_sectors(bs1);
|
||||
buf1 = blk_blockalign(blk1, IO_BUF_SIZE);
|
||||
buf2 = blk_blockalign(blk2, IO_BUF_SIZE);
|
||||
total_sectors1 = blk_nb_sectors(blk1);
|
||||
if (total_sectors1 < 0) {
|
||||
error_report("Can't get size of %s: %s",
|
||||
filename1, strerror(-total_sectors1));
|
||||
ret = 4;
|
||||
goto out;
|
||||
}
|
||||
total_sectors2 = bdrv_nb_sectors(bs2);
|
||||
total_sectors2 = blk_nb_sectors(blk2);
|
||||
if (total_sectors2 < 0) {
|
||||
error_report("Can't get size of %s: %s",
|
||||
filename2, strerror(-total_sectors2));
|
||||
|
@ -1189,7 +1183,7 @@ static int img_compare(int argc, char **argv)
|
|||
|
||||
if (allocated1 == allocated2) {
|
||||
if (allocated1) {
|
||||
ret = bdrv_read(bs1, sector_num, buf1, nb_sectors);
|
||||
ret = blk_read(blk1, sector_num, buf1, nb_sectors);
|
||||
if (ret < 0) {
|
||||
error_report("Error while reading offset %" PRId64 " of %s:"
|
||||
" %s", sectors_to_bytes(sector_num), filename1,
|
||||
|
@ -1197,7 +1191,7 @@ static int img_compare(int argc, char **argv)
|
|||
ret = 4;
|
||||
goto out;
|
||||
}
|
||||
ret = bdrv_read(bs2, sector_num, buf2, nb_sectors);
|
||||
ret = blk_read(blk2, sector_num, buf2, nb_sectors);
|
||||
if (ret < 0) {
|
||||
error_report("Error while reading offset %" PRId64
|
||||
" of %s: %s", sectors_to_bytes(sector_num),
|
||||
|
@ -1224,10 +1218,10 @@ static int img_compare(int argc, char **argv)
|
|||
}
|
||||
|
||||
if (allocated1) {
|
||||
ret = check_empty_sectors(bs1, sector_num, nb_sectors,
|
||||
ret = check_empty_sectors(blk1, sector_num, nb_sectors,
|
||||
filename1, buf1, quiet);
|
||||
} else {
|
||||
ret = check_empty_sectors(bs2, sector_num, nb_sectors,
|
||||
ret = check_empty_sectors(blk2, sector_num, nb_sectors,
|
||||
filename2, buf1, quiet);
|
||||
}
|
||||
if (ret) {
|
||||
|
@ -1244,18 +1238,18 @@ static int img_compare(int argc, char **argv)
|
|||
}
|
||||
|
||||
if (total_sectors1 != total_sectors2) {
|
||||
BlockDriverState *bs_over;
|
||||
BlockBackend *blk_over;
|
||||
int64_t total_sectors_over;
|
||||
const char *filename_over;
|
||||
|
||||
qprintf(quiet, "Warning: Image size mismatch!\n");
|
||||
if (total_sectors1 > total_sectors2) {
|
||||
total_sectors_over = total_sectors1;
|
||||
bs_over = bs1;
|
||||
blk_over = blk1;
|
||||
filename_over = filename1;
|
||||
} else {
|
||||
total_sectors_over = total_sectors2;
|
||||
bs_over = bs2;
|
||||
blk_over = blk2;
|
||||
filename_over = filename2;
|
||||
}
|
||||
|
||||
|
@ -1264,7 +1258,7 @@ static int img_compare(int argc, char **argv)
|
|||
if (nb_sectors <= 0) {
|
||||
break;
|
||||
}
|
||||
ret = bdrv_is_allocated_above(bs_over, NULL, sector_num,
|
||||
ret = bdrv_is_allocated_above(blk_bs(blk_over), NULL, sector_num,
|
||||
nb_sectors, &pnum);
|
||||
if (ret < 0) {
|
||||
ret = 3;
|
||||
|
@ -1275,7 +1269,7 @@ static int img_compare(int argc, char **argv)
|
|||
}
|
||||
nb_sectors = pnum;
|
||||
if (ret) {
|
||||
ret = check_empty_sectors(bs_over, sector_num, nb_sectors,
|
||||
ret = check_empty_sectors(blk_over, sector_num, nb_sectors,
|
||||
filename_over, buf1, quiet);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
|
@ -1484,7 +1478,7 @@ static int img_convert(int argc, char **argv)
|
|||
goto out;
|
||||
}
|
||||
bs[bs_i] = blk_bs(blk[bs_i]);
|
||||
bs_sectors[bs_i] = bdrv_nb_sectors(bs[bs_i]);
|
||||
bs_sectors[bs_i] = blk_nb_sectors(blk[bs_i]);
|
||||
if (bs_sectors[bs_i] < 0) {
|
||||
error_report("Could not get size of %s: %s",
|
||||
argv[optind + bs_i], strerror(-bs_sectors[bs_i]));
|
||||
|
@ -1524,41 +1518,44 @@ static int img_convert(int argc, char **argv)
|
|||
goto out;
|
||||
}
|
||||
|
||||
proto_drv = bdrv_find_protocol(out_filename, true);
|
||||
proto_drv = bdrv_find_protocol(out_filename, true, &local_err);
|
||||
if (!proto_drv) {
|
||||
error_report("Unknown protocol '%s'", out_filename);
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!drv->create_opts) {
|
||||
error_report("Format driver '%s' does not support image creation",
|
||||
drv->format_name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
if (!skip_create) {
|
||||
if (!drv->create_opts) {
|
||||
error_report("Format driver '%s' does not support image creation",
|
||||
drv->format_name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!proto_drv->create_opts) {
|
||||
error_report("Protocol driver '%s' does not support image creation",
|
||||
proto_drv->format_name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
if (!proto_drv->create_opts) {
|
||||
error_report("Protocol driver '%s' does not support image creation",
|
||||
proto_drv->format_name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
create_opts = qemu_opts_append(create_opts, drv->create_opts);
|
||||
create_opts = qemu_opts_append(create_opts, proto_drv->create_opts);
|
||||
create_opts = qemu_opts_append(create_opts, drv->create_opts);
|
||||
create_opts = qemu_opts_append(create_opts, proto_drv->create_opts);
|
||||
|
||||
opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
|
||||
if (options && qemu_opts_do_parse(opts, options, NULL)) {
|
||||
error_report("Invalid options for file format '%s'", out_fmt);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
|
||||
if (options && qemu_opts_do_parse(opts, options, NULL)) {
|
||||
error_report("Invalid options for file format '%s'", out_fmt);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_sectors * 512);
|
||||
ret = add_old_style_options(out_fmt, opts, out_baseimg, NULL);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_sectors * 512);
|
||||
ret = add_old_style_options(out_fmt, opts, out_baseimg, NULL);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get backing file name if -o backing_file was used */
|
||||
|
@ -1633,10 +1630,10 @@ static int img_convert(int argc, char **argv)
|
|||
out_bs->bl.discard_alignment))
|
||||
);
|
||||
|
||||
buf = qemu_blockalign(out_bs, bufsectors * BDRV_SECTOR_SIZE);
|
||||
buf = blk_blockalign(out_blk, bufsectors * BDRV_SECTOR_SIZE);
|
||||
|
||||
if (skip_create) {
|
||||
int64_t output_sectors = bdrv_nb_sectors(out_bs);
|
||||
int64_t output_sectors = blk_nb_sectors(out_blk);
|
||||
if (output_sectors < 0) {
|
||||
error_report("unable to get output image length: %s\n",
|
||||
strerror(-output_sectors));
|
||||
|
@ -1704,7 +1701,7 @@ static int img_convert(int argc, char **argv)
|
|||
nlow = remainder > bs_sectors[bs_i] - bs_num
|
||||
? bs_sectors[bs_i] - bs_num : remainder;
|
||||
|
||||
ret = bdrv_read(bs[bs_i], bs_num, buf2, nlow);
|
||||
ret = blk_read(blk[bs_i], bs_num, buf2, nlow);
|
||||
if (ret < 0) {
|
||||
error_report("error while reading sector %" PRId64 ": %s",
|
||||
bs_num, strerror(-ret));
|
||||
|
@ -1719,7 +1716,7 @@ static int img_convert(int argc, char **argv)
|
|||
assert (remainder == 0);
|
||||
|
||||
if (!buffer_is_zero(buf, n * BDRV_SECTOR_SIZE)) {
|
||||
ret = bdrv_write_compressed(out_bs, sector_num, buf, n);
|
||||
ret = blk_write_compressed(out_blk, sector_num, buf, n);
|
||||
if (ret != 0) {
|
||||
error_report("error while compressing sector %" PRId64
|
||||
": %s", sector_num, strerror(-ret));
|
||||
|
@ -1730,7 +1727,7 @@ static int img_convert(int argc, char **argv)
|
|||
qemu_progress_print(100.0 * sector_num / total_sectors, 0);
|
||||
}
|
||||
/* signal EOF to align */
|
||||
bdrv_write_compressed(out_bs, 0, NULL, 0);
|
||||
blk_write_compressed(out_blk, 0, NULL, 0);
|
||||
} else {
|
||||
int64_t sectors_to_read, sectors_read, sector_num_next_status;
|
||||
bool count_allocated_sectors;
|
||||
|
@ -1831,7 +1828,7 @@ restart:
|
|||
}
|
||||
|
||||
n1 = n;
|
||||
ret = bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n);
|
||||
ret = blk_read(blk[bs_i], sector_num - bs_offset, buf, n);
|
||||
if (ret < 0) {
|
||||
error_report("error while reading sector %" PRId64 ": %s",
|
||||
sector_num - bs_offset, strerror(-ret));
|
||||
|
@ -1844,7 +1841,7 @@ restart:
|
|||
while (n > 0) {
|
||||
if (!has_zero_init ||
|
||||
is_allocated_sectors_min(buf1, n, &n1, min_sparse)) {
|
||||
ret = bdrv_write(out_bs, sector_num, buf1, n1);
|
||||
ret = blk_write(out_blk, sector_num, buf1, n1);
|
||||
if (ret < 0) {
|
||||
error_report("error while writing sector %" PRId64
|
||||
": %s", sector_num, strerror(-ret));
|
||||
|
@ -2268,7 +2265,7 @@ static int img_map(int argc, char **argv)
|
|||
printf("%-16s%-16s%-16s%s\n", "Offset", "Length", "Mapped to", "File");
|
||||
}
|
||||
|
||||
length = bdrv_getlength(bs);
|
||||
length = blk_getlength(blk);
|
||||
while (curr.start + curr.length < length) {
|
||||
int64_t nsectors_left;
|
||||
int64_t sector_num;
|
||||
|
@ -2437,8 +2434,7 @@ static int img_snapshot(int argc, char **argv)
|
|||
static int img_rebase(int argc, char **argv)
|
||||
{
|
||||
BlockBackend *blk = NULL, *blk_old_backing = NULL, *blk_new_backing = NULL;
|
||||
BlockDriverState *bs = NULL, *bs_old_backing = NULL, *bs_new_backing = NULL;
|
||||
BlockDriver *old_backing_drv, *new_backing_drv;
|
||||
BlockDriverState *bs = NULL;
|
||||
char *filename;
|
||||
const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg;
|
||||
int c, flags, src_flags, ret;
|
||||
|
@ -2532,22 +2528,8 @@ static int img_rebase(int argc, char **argv)
|
|||
}
|
||||
bs = blk_bs(blk);
|
||||
|
||||
/* Find the right drivers for the backing files */
|
||||
old_backing_drv = NULL;
|
||||
new_backing_drv = NULL;
|
||||
|
||||
if (!unsafe && bs->backing_format[0] != '\0') {
|
||||
old_backing_drv = bdrv_find_format(bs->backing_format);
|
||||
if (old_backing_drv == NULL) {
|
||||
error_report("Invalid format name: '%s'", bs->backing_format);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (out_basefmt != NULL) {
|
||||
new_backing_drv = bdrv_find_format(out_basefmt);
|
||||
if (new_backing_drv == NULL) {
|
||||
if (bdrv_find_format(out_basefmt) == NULL) {
|
||||
error_report("Invalid format name: '%s'", out_basefmt);
|
||||
ret = -1;
|
||||
goto out;
|
||||
|
@ -2557,24 +2539,34 @@ static int img_rebase(int argc, char **argv)
|
|||
/* For safe rebasing we need to compare old and new backing file */
|
||||
if (!unsafe) {
|
||||
char backing_name[PATH_MAX];
|
||||
QDict *options = NULL;
|
||||
|
||||
if (bs->backing_format[0] != '\0') {
|
||||
options = qdict_new();
|
||||
qdict_put(options, "driver", qstring_from_str(bs->backing_format));
|
||||
}
|
||||
|
||||
blk_old_backing = blk_new_with_bs("old_backing", &error_abort);
|
||||
bs_old_backing = blk_bs(blk_old_backing);
|
||||
bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name));
|
||||
ret = bdrv_open(&bs_old_backing, backing_name, NULL, NULL, src_flags,
|
||||
old_backing_drv, &local_err);
|
||||
if (ret) {
|
||||
blk_old_backing = blk_new_open("old_backing", backing_name, NULL,
|
||||
options, src_flags, &local_err);
|
||||
if (!blk_old_backing) {
|
||||
error_report("Could not open old backing file '%s': %s",
|
||||
backing_name, error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (out_baseimg[0]) {
|
||||
blk_new_backing = blk_new_with_bs("new_backing", &error_abort);
|
||||
bs_new_backing = blk_bs(blk_new_backing);
|
||||
ret = bdrv_open(&bs_new_backing, out_baseimg, NULL, NULL, src_flags,
|
||||
new_backing_drv, &local_err);
|
||||
if (ret) {
|
||||
if (out_basefmt) {
|
||||
options = qdict_new();
|
||||
qdict_put(options, "driver", qstring_from_str(out_basefmt));
|
||||
} else {
|
||||
options = NULL;
|
||||
}
|
||||
|
||||
blk_new_backing = blk_new_open("new_backing", out_baseimg, NULL,
|
||||
options, src_flags, &local_err);
|
||||
if (!blk_new_backing) {
|
||||
error_report("Could not open new backing file '%s': %s",
|
||||
out_baseimg, error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
|
@ -2602,17 +2594,17 @@ static int img_rebase(int argc, char **argv)
|
|||
uint8_t * buf_new;
|
||||
float local_progress = 0;
|
||||
|
||||
buf_old = qemu_blockalign(bs, IO_BUF_SIZE);
|
||||
buf_new = qemu_blockalign(bs, IO_BUF_SIZE);
|
||||
buf_old = blk_blockalign(blk, IO_BUF_SIZE);
|
||||
buf_new = blk_blockalign(blk, IO_BUF_SIZE);
|
||||
|
||||
num_sectors = bdrv_nb_sectors(bs);
|
||||
num_sectors = blk_nb_sectors(blk);
|
||||
if (num_sectors < 0) {
|
||||
error_report("Could not get size of '%s': %s",
|
||||
filename, strerror(-num_sectors));
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
old_backing_num_sectors = bdrv_nb_sectors(bs_old_backing);
|
||||
old_backing_num_sectors = blk_nb_sectors(blk_old_backing);
|
||||
if (old_backing_num_sectors < 0) {
|
||||
char backing_name[PATH_MAX];
|
||||
|
||||
|
@ -2622,8 +2614,8 @@ static int img_rebase(int argc, char **argv)
|
|||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
if (bs_new_backing) {
|
||||
new_backing_num_sectors = bdrv_nb_sectors(bs_new_backing);
|
||||
if (blk_new_backing) {
|
||||
new_backing_num_sectors = blk_nb_sectors(blk_new_backing);
|
||||
if (new_backing_num_sectors < 0) {
|
||||
error_report("Could not get size of '%s': %s",
|
||||
out_baseimg, strerror(-new_backing_num_sectors));
|
||||
|
@ -2668,21 +2660,21 @@ static int img_rebase(int argc, char **argv)
|
|||
n = old_backing_num_sectors - sector;
|
||||
}
|
||||
|
||||
ret = bdrv_read(bs_old_backing, sector, buf_old, n);
|
||||
ret = blk_read(blk_old_backing, sector, buf_old, n);
|
||||
if (ret < 0) {
|
||||
error_report("error while reading from old backing file");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (sector >= new_backing_num_sectors || !bs_new_backing) {
|
||||
if (sector >= new_backing_num_sectors || !blk_new_backing) {
|
||||
memset(buf_new, 0, n * BDRV_SECTOR_SIZE);
|
||||
} else {
|
||||
if (sector + n > new_backing_num_sectors) {
|
||||
n = new_backing_num_sectors - sector;
|
||||
}
|
||||
|
||||
ret = bdrv_read(bs_new_backing, sector, buf_new, n);
|
||||
ret = blk_read(blk_new_backing, sector, buf_new, n);
|
||||
if (ret < 0) {
|
||||
error_report("error while reading from new backing file");
|
||||
goto out;
|
||||
|
@ -2698,8 +2690,8 @@ static int img_rebase(int argc, char **argv)
|
|||
if (compare_sectors(buf_old + written * 512,
|
||||
buf_new + written * 512, n - written, &pnum))
|
||||
{
|
||||
ret = bdrv_write(bs, sector + written,
|
||||
buf_old + written * 512, pnum);
|
||||
ret = blk_write(blk, sector + written,
|
||||
buf_old + written * 512, pnum);
|
||||
if (ret < 0) {
|
||||
error_report("Error while writing to COW image: %s",
|
||||
strerror(-ret));
|
||||
|
@ -2764,7 +2756,6 @@ static int img_resize(int argc, char **argv)
|
|||
int64_t n, total_size;
|
||||
bool quiet = false;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
QemuOpts *param;
|
||||
static QemuOptsList resize_options = {
|
||||
.name = "resize_options",
|
||||
|
@ -2846,10 +2837,9 @@ static int img_resize(int argc, char **argv)
|
|||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
bs = blk_bs(blk);
|
||||
|
||||
if (relative) {
|
||||
total_size = bdrv_getlength(bs) + n * relative;
|
||||
total_size = blk_getlength(blk) + n * relative;
|
||||
} else {
|
||||
total_size = n;
|
||||
}
|
||||
|
@ -2859,7 +2849,7 @@ static int img_resize(int argc, char **argv)
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = bdrv_truncate(bs, total_size);
|
||||
ret = blk_truncate(blk, total_size);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
qprintf(quiet, "Image resized.\n");
|
||||
|
|
250
qemu-io-cmds.c
250
qemu-io-cmds.c
|
@ -9,10 +9,13 @@
|
|||
*/
|
||||
|
||||
#include "qemu-io.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "block/block.h"
|
||||
#include "block/block_int.h" /* for info_f() */
|
||||
#include "block/qapi.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
|
||||
#define CMD_NOFILE_OK 0x01
|
||||
|
||||
|
@ -40,24 +43,24 @@ int qemuio_command_usage(const cmdinfo_t *ci)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int init_check_command(BlockDriverState *bs, const cmdinfo_t *ct)
|
||||
static int init_check_command(BlockBackend *blk, const cmdinfo_t *ct)
|
||||
{
|
||||
if (ct->flags & CMD_FLAG_GLOBAL) {
|
||||
return 1;
|
||||
}
|
||||
if (!(ct->flags & CMD_NOFILE_OK) && !bs) {
|
||||
if (!(ct->flags & CMD_NOFILE_OK) && !blk) {
|
||||
fprintf(stderr, "no file open, try 'help open'\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int command(BlockDriverState *bs, const cmdinfo_t *ct, int argc,
|
||||
static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc,
|
||||
char **argv)
|
||||
{
|
||||
char *cmd = argv[0];
|
||||
|
||||
if (!init_check_command(bs, ct)) {
|
||||
if (!init_check_command(blk, ct)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -78,7 +81,7 @@ static int command(BlockDriverState *bs, const cmdinfo_t *ct, int argc,
|
|||
return 0;
|
||||
}
|
||||
optind = 0;
|
||||
return ct->cfunc(bs, argc, argv);
|
||||
return ct->cfunc(blk, argc, argv);
|
||||
}
|
||||
|
||||
static const cmdinfo_t *find_command(const char *cmd)
|
||||
|
@ -267,14 +270,14 @@ static int parse_pattern(const char *arg)
|
|||
*/
|
||||
|
||||
#define MISALIGN_OFFSET 16
|
||||
static void *qemu_io_alloc(BlockDriverState *bs, size_t len, int pattern)
|
||||
static void *qemu_io_alloc(BlockBackend *blk, size_t len, int pattern)
|
||||
{
|
||||
void *buf;
|
||||
|
||||
if (qemuio_misalign) {
|
||||
len += MISALIGN_OFFSET;
|
||||
}
|
||||
buf = qemu_blockalign(bs, len);
|
||||
buf = blk_blockalign(blk, len);
|
||||
memset(buf, pattern, len);
|
||||
if (qemuio_misalign) {
|
||||
buf += MISALIGN_OFFSET;
|
||||
|
@ -340,7 +343,7 @@ static void print_report(const char *op, struct timeval *t, int64_t offset,
|
|||
* vector matching it.
|
||||
*/
|
||||
static void *
|
||||
create_iovec(BlockDriverState *bs, QEMUIOVector *qiov, char **argv, int nr_iov,
|
||||
create_iovec(BlockBackend *blk, QEMUIOVector *qiov, char **argv, int nr_iov,
|
||||
int pattern)
|
||||
{
|
||||
size_t *sizes = g_new0(size_t, nr_iov);
|
||||
|
@ -377,7 +380,7 @@ create_iovec(BlockDriverState *bs, QEMUIOVector *qiov, char **argv, int nr_iov,
|
|||
|
||||
qemu_iovec_init(qiov, nr_iov);
|
||||
|
||||
buf = p = qemu_io_alloc(bs, count, pattern);
|
||||
buf = p = qemu_io_alloc(blk, count, pattern);
|
||||
|
||||
for (i = 0; i < nr_iov; i++) {
|
||||
qemu_iovec_add(qiov, p, sizes[i]);
|
||||
|
@ -389,12 +392,12 @@ fail:
|
|||
return buf;
|
||||
}
|
||||
|
||||
static int do_read(BlockDriverState *bs, char *buf, int64_t offset, int count,
|
||||
static int do_read(BlockBackend *blk, char *buf, int64_t offset, int count,
|
||||
int *total)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, count >> 9);
|
||||
ret = blk_read(blk, offset >> 9, (uint8_t *)buf, count >> 9);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -402,12 +405,12 @@ static int do_read(BlockDriverState *bs, char *buf, int64_t offset, int count,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int do_write(BlockDriverState *bs, char *buf, int64_t offset, int count,
|
||||
static int do_write(BlockBackend *blk, char *buf, int64_t offset, int count,
|
||||
int *total)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bdrv_write(bs, offset >> 9, (uint8_t *)buf, count >> 9);
|
||||
ret = blk_write(blk, offset >> 9, (uint8_t *)buf, count >> 9);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -415,20 +418,20 @@ static int do_write(BlockDriverState *bs, char *buf, int64_t offset, int count,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int do_pread(BlockDriverState *bs, char *buf, int64_t offset, int count,
|
||||
static int do_pread(BlockBackend *blk, char *buf, int64_t offset, int count,
|
||||
int *total)
|
||||
{
|
||||
*total = bdrv_pread(bs, offset, (uint8_t *)buf, count);
|
||||
*total = blk_pread(blk, offset, (uint8_t *)buf, count);
|
||||
if (*total < 0) {
|
||||
return *total;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_pwrite(BlockDriverState *bs, char *buf, int64_t offset, int count,
|
||||
static int do_pwrite(BlockBackend *blk, char *buf, int64_t offset, int count,
|
||||
int *total)
|
||||
{
|
||||
*total = bdrv_pwrite(bs, offset, (uint8_t *)buf, count);
|
||||
*total = blk_pwrite(blk, offset, (uint8_t *)buf, count);
|
||||
if (*total < 0) {
|
||||
return *total;
|
||||
}
|
||||
|
@ -436,7 +439,7 @@ static int do_pwrite(BlockDriverState *bs, char *buf, int64_t offset, int count,
|
|||
}
|
||||
|
||||
typedef struct {
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *blk;
|
||||
int64_t offset;
|
||||
int count;
|
||||
int *total;
|
||||
|
@ -448,8 +451,8 @@ static void coroutine_fn co_write_zeroes_entry(void *opaque)
|
|||
{
|
||||
CoWriteZeroes *data = opaque;
|
||||
|
||||
data->ret = bdrv_co_write_zeroes(data->bs, data->offset / BDRV_SECTOR_SIZE,
|
||||
data->count / BDRV_SECTOR_SIZE, 0);
|
||||
data->ret = blk_co_write_zeroes(data->blk, data->offset / BDRV_SECTOR_SIZE,
|
||||
data->count / BDRV_SECTOR_SIZE, 0);
|
||||
data->done = true;
|
||||
if (data->ret < 0) {
|
||||
*data->total = data->ret;
|
||||
|
@ -459,12 +462,12 @@ static void coroutine_fn co_write_zeroes_entry(void *opaque)
|
|||
*data->total = data->count;
|
||||
}
|
||||
|
||||
static int do_co_write_zeroes(BlockDriverState *bs, int64_t offset, int count,
|
||||
static int do_co_write_zeroes(BlockBackend *blk, int64_t offset, int count,
|
||||
int *total)
|
||||
{
|
||||
Coroutine *co;
|
||||
CoWriteZeroes data = {
|
||||
.bs = bs,
|
||||
.blk = blk,
|
||||
.offset = offset,
|
||||
.count = count,
|
||||
.total = total,
|
||||
|
@ -474,7 +477,7 @@ static int do_co_write_zeroes(BlockDriverState *bs, int64_t offset, int count,
|
|||
co = qemu_coroutine_create(co_write_zeroes_entry);
|
||||
qemu_coroutine_enter(co, &data);
|
||||
while (!data.done) {
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
aio_poll(blk_get_aio_context(blk), true);
|
||||
}
|
||||
if (data.ret < 0) {
|
||||
return data.ret;
|
||||
|
@ -483,12 +486,12 @@ static int do_co_write_zeroes(BlockDriverState *bs, int64_t offset, int count,
|
|||
}
|
||||
}
|
||||
|
||||
static int do_write_compressed(BlockDriverState *bs, char *buf, int64_t offset,
|
||||
static int do_write_compressed(BlockBackend *blk, char *buf, int64_t offset,
|
||||
int count, int *total)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bdrv_write_compressed(bs, offset >> 9, (uint8_t *)buf, count >> 9);
|
||||
ret = blk_write_compressed(blk, offset >> 9, (uint8_t *)buf, count >> 9);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -496,20 +499,20 @@ static int do_write_compressed(BlockDriverState *bs, char *buf, int64_t offset,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int do_load_vmstate(BlockDriverState *bs, char *buf, int64_t offset,
|
||||
static int do_load_vmstate(BlockBackend *blk, char *buf, int64_t offset,
|
||||
int count, int *total)
|
||||
{
|
||||
*total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count);
|
||||
*total = blk_load_vmstate(blk, (uint8_t *)buf, offset, count);
|
||||
if (*total < 0) {
|
||||
return *total;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_save_vmstate(BlockDriverState *bs, char *buf, int64_t offset,
|
||||
static int do_save_vmstate(BlockBackend *blk, char *buf, int64_t offset,
|
||||
int count, int *total)
|
||||
{
|
||||
*total = bdrv_save_vmstate(bs, (uint8_t *)buf, offset, count);
|
||||
*total = blk_save_vmstate(blk, (uint8_t *)buf, offset, count);
|
||||
if (*total < 0) {
|
||||
return *total;
|
||||
}
|
||||
|
@ -522,13 +525,13 @@ static void aio_rw_done(void *opaque, int ret)
|
|||
*(int *)opaque = ret;
|
||||
}
|
||||
|
||||
static int do_aio_readv(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
static int do_aio_readv(BlockBackend *blk, QEMUIOVector *qiov,
|
||||
int64_t offset, int *total)
|
||||
{
|
||||
int async_ret = NOT_DONE;
|
||||
|
||||
bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9,
|
||||
aio_rw_done, &async_ret);
|
||||
blk_aio_readv(blk, offset >> 9, qiov, qiov->size >> 9,
|
||||
aio_rw_done, &async_ret);
|
||||
while (async_ret == NOT_DONE) {
|
||||
main_loop_wait(false);
|
||||
}
|
||||
|
@ -537,13 +540,13 @@ static int do_aio_readv(BlockDriverState *bs, QEMUIOVector *qiov,
|
|||
return async_ret < 0 ? async_ret : 1;
|
||||
}
|
||||
|
||||
static int do_aio_writev(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
static int do_aio_writev(BlockBackend *blk, QEMUIOVector *qiov,
|
||||
int64_t offset, int *total)
|
||||
{
|
||||
int async_ret = NOT_DONE;
|
||||
|
||||
bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9,
|
||||
aio_rw_done, &async_ret);
|
||||
blk_aio_writev(blk, offset >> 9, qiov, qiov->size >> 9,
|
||||
aio_rw_done, &async_ret);
|
||||
while (async_ret == NOT_DONE) {
|
||||
main_loop_wait(false);
|
||||
}
|
||||
|
@ -567,7 +570,7 @@ static void multiwrite_cb(void *opaque, int ret)
|
|||
}
|
||||
}
|
||||
|
||||
static int do_aio_multiwrite(BlockDriverState *bs, BlockRequest* reqs,
|
||||
static int do_aio_multiwrite(BlockBackend *blk, BlockRequest* reqs,
|
||||
int num_reqs, int *total)
|
||||
{
|
||||
int i, ret;
|
||||
|
@ -583,7 +586,7 @@ static int do_aio_multiwrite(BlockDriverState *bs, BlockRequest* reqs,
|
|||
*total += reqs[i].qiov->size;
|
||||
}
|
||||
|
||||
ret = bdrv_aio_multiwrite(bs, reqs, num_reqs);
|
||||
ret = blk_aio_multiwrite(blk, reqs, num_reqs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -609,7 +612,7 @@ static void read_help(void)
|
|||
" -b, -- read from the VM state rather than the virtual disk\n"
|
||||
" -C, -- report statistics in a machine parsable format\n"
|
||||
" -l, -- length for pattern verification (only with -P)\n"
|
||||
" -p, -- use bdrv_pread to read the file\n"
|
||||
" -p, -- use blk_pread to read the file\n"
|
||||
" -P, -- use a pattern to verify read data\n"
|
||||
" -q, -- quiet mode, do not show I/O statistics\n"
|
||||
" -s, -- start offset for pattern verification (only with -P)\n"
|
||||
|
@ -617,7 +620,7 @@ static void read_help(void)
|
|||
"\n");
|
||||
}
|
||||
|
||||
static int read_f(BlockDriverState *bs, int argc, char **argv);
|
||||
static int read_f(BlockBackend *blk, int argc, char **argv);
|
||||
|
||||
static const cmdinfo_t read_cmd = {
|
||||
.name = "read",
|
||||
|
@ -630,7 +633,7 @@ static const cmdinfo_t read_cmd = {
|
|||
.help = read_help,
|
||||
};
|
||||
|
||||
static int read_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int read_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
struct timeval t1, t2;
|
||||
int Cflag = 0, pflag = 0, qflag = 0, vflag = 0;
|
||||
|
@ -736,15 +739,15 @@ static int read_f(BlockDriverState *bs, int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
buf = qemu_io_alloc(bs, count, 0xab);
|
||||
buf = qemu_io_alloc(blk, count, 0xab);
|
||||
|
||||
gettimeofday(&t1, NULL);
|
||||
if (pflag) {
|
||||
cnt = do_pread(bs, buf, offset, count, &total);
|
||||
cnt = do_pread(blk, buf, offset, count, &total);
|
||||
} else if (bflag) {
|
||||
cnt = do_load_vmstate(bs, buf, offset, count, &total);
|
||||
cnt = do_load_vmstate(blk, buf, offset, count, &total);
|
||||
} else {
|
||||
cnt = do_read(bs, buf, offset, count, &total);
|
||||
cnt = do_read(blk, buf, offset, count, &total);
|
||||
}
|
||||
gettimeofday(&t2, NULL);
|
||||
|
||||
|
@ -801,7 +804,7 @@ static void readv_help(void)
|
|||
"\n");
|
||||
}
|
||||
|
||||
static int readv_f(BlockDriverState *bs, int argc, char **argv);
|
||||
static int readv_f(BlockBackend *blk, int argc, char **argv);
|
||||
|
||||
static const cmdinfo_t readv_cmd = {
|
||||
.name = "readv",
|
||||
|
@ -813,7 +816,7 @@ static const cmdinfo_t readv_cmd = {
|
|||
.help = readv_help,
|
||||
};
|
||||
|
||||
static int readv_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int readv_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
struct timeval t1, t2;
|
||||
int Cflag = 0, qflag = 0, vflag = 0;
|
||||
|
@ -869,13 +872,13 @@ static int readv_f(BlockDriverState *bs, int argc, char **argv)
|
|||
}
|
||||
|
||||
nr_iov = argc - optind;
|
||||
buf = create_iovec(bs, &qiov, &argv[optind], nr_iov, 0xab);
|
||||
buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, 0xab);
|
||||
if (buf == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
gettimeofday(&t1, NULL);
|
||||
cnt = do_aio_readv(bs, &qiov, offset, &total);
|
||||
cnt = do_aio_readv(blk, &qiov, offset, &total);
|
||||
gettimeofday(&t2, NULL);
|
||||
|
||||
if (cnt < 0) {
|
||||
|
@ -923,16 +926,16 @@ static void write_help(void)
|
|||
" Writes into a segment of the currently open file, using a buffer\n"
|
||||
" filled with a set pattern (0xcdcdcdcd).\n"
|
||||
" -b, -- write to the VM state rather than the virtual disk\n"
|
||||
" -c, -- write compressed data with bdrv_write_compressed\n"
|
||||
" -p, -- use bdrv_pwrite to write the file\n"
|
||||
" -c, -- write compressed data with blk_write_compressed\n"
|
||||
" -p, -- use blk_pwrite to write the file\n"
|
||||
" -P, -- use different pattern to fill file\n"
|
||||
" -C, -- report statistics in a machine parsable format\n"
|
||||
" -q, -- quiet mode, do not show I/O statistics\n"
|
||||
" -z, -- write zeroes using bdrv_co_write_zeroes\n"
|
||||
" -z, -- write zeroes using blk_co_write_zeroes\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
static int write_f(BlockDriverState *bs, int argc, char **argv);
|
||||
static int write_f(BlockBackend *blk, int argc, char **argv);
|
||||
|
||||
static const cmdinfo_t write_cmd = {
|
||||
.name = "write",
|
||||
|
@ -945,7 +948,7 @@ static const cmdinfo_t write_cmd = {
|
|||
.help = write_help,
|
||||
};
|
||||
|
||||
static int write_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int write_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
struct timeval t1, t2;
|
||||
int Cflag = 0, pflag = 0, qflag = 0, bflag = 0, Pflag = 0, zflag = 0;
|
||||
|
@ -1032,20 +1035,20 @@ static int write_f(BlockDriverState *bs, int argc, char **argv)
|
|||
}
|
||||
|
||||
if (!zflag) {
|
||||
buf = qemu_io_alloc(bs, count, pattern);
|
||||
buf = qemu_io_alloc(blk, count, pattern);
|
||||
}
|
||||
|
||||
gettimeofday(&t1, NULL);
|
||||
if (pflag) {
|
||||
cnt = do_pwrite(bs, buf, offset, count, &total);
|
||||
cnt = do_pwrite(blk, buf, offset, count, &total);
|
||||
} else if (bflag) {
|
||||
cnt = do_save_vmstate(bs, buf, offset, count, &total);
|
||||
cnt = do_save_vmstate(blk, buf, offset, count, &total);
|
||||
} else if (zflag) {
|
||||
cnt = do_co_write_zeroes(bs, offset, count, &total);
|
||||
cnt = do_co_write_zeroes(blk, offset, count, &total);
|
||||
} else if (cflag) {
|
||||
cnt = do_write_compressed(bs, buf, offset, count, &total);
|
||||
cnt = do_write_compressed(blk, buf, offset, count, &total);
|
||||
} else {
|
||||
cnt = do_write(bs, buf, offset, count, &total);
|
||||
cnt = do_write(blk, buf, offset, count, &total);
|
||||
}
|
||||
gettimeofday(&t2, NULL);
|
||||
|
||||
|
@ -1088,7 +1091,7 @@ writev_help(void)
|
|||
"\n");
|
||||
}
|
||||
|
||||
static int writev_f(BlockDriverState *bs, int argc, char **argv);
|
||||
static int writev_f(BlockBackend *blk, int argc, char **argv);
|
||||
|
||||
static const cmdinfo_t writev_cmd = {
|
||||
.name = "writev",
|
||||
|
@ -1100,7 +1103,7 @@ static const cmdinfo_t writev_cmd = {
|
|||
.help = writev_help,
|
||||
};
|
||||
|
||||
static int writev_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int writev_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
struct timeval t1, t2;
|
||||
int Cflag = 0, qflag = 0;
|
||||
|
@ -1150,13 +1153,13 @@ static int writev_f(BlockDriverState *bs, int argc, char **argv)
|
|||
}
|
||||
|
||||
nr_iov = argc - optind;
|
||||
buf = create_iovec(bs, &qiov, &argv[optind], nr_iov, pattern);
|
||||
buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, pattern);
|
||||
if (buf == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
gettimeofday(&t1, NULL);
|
||||
cnt = do_aio_writev(bs, &qiov, offset, &total);
|
||||
cnt = do_aio_writev(blk, &qiov, offset, &total);
|
||||
gettimeofday(&t2, NULL);
|
||||
|
||||
if (cnt < 0) {
|
||||
|
@ -1197,7 +1200,7 @@ static void multiwrite_help(void)
|
|||
"\n");
|
||||
}
|
||||
|
||||
static int multiwrite_f(BlockDriverState *bs, int argc, char **argv);
|
||||
static int multiwrite_f(BlockBackend *blk, int argc, char **argv);
|
||||
|
||||
static const cmdinfo_t multiwrite_cmd = {
|
||||
.name = "multiwrite",
|
||||
|
@ -1209,7 +1212,7 @@ static const cmdinfo_t multiwrite_cmd = {
|
|||
.help = multiwrite_help,
|
||||
};
|
||||
|
||||
static int multiwrite_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int multiwrite_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
struct timeval t1, t2;
|
||||
int Cflag = 0, qflag = 0;
|
||||
|
@ -1290,7 +1293,7 @@ static int multiwrite_f(BlockDriverState *bs, int argc, char **argv)
|
|||
nr_iov = j - optind;
|
||||
|
||||
/* Build request */
|
||||
buf[i] = create_iovec(bs, &qiovs[i], &argv[optind], nr_iov, pattern);
|
||||
buf[i] = create_iovec(blk, &qiovs[i], &argv[optind], nr_iov, pattern);
|
||||
if (buf[i] == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -1308,7 +1311,7 @@ static int multiwrite_f(BlockDriverState *bs, int argc, char **argv)
|
|||
nr_reqs = i;
|
||||
|
||||
gettimeofday(&t1, NULL);
|
||||
cnt = do_aio_multiwrite(bs, reqs, nr_reqs, &total);
|
||||
cnt = do_aio_multiwrite(blk, reqs, nr_reqs, &total);
|
||||
gettimeofday(&t2, NULL);
|
||||
|
||||
if (cnt < 0) {
|
||||
|
@ -1337,6 +1340,7 @@ out:
|
|||
}
|
||||
|
||||
struct aio_ctx {
|
||||
BlockBackend *blk;
|
||||
QEMUIOVector qiov;
|
||||
int64_t offset;
|
||||
char *buf;
|
||||
|
@ -1344,6 +1348,7 @@ struct aio_ctx {
|
|||
int vflag;
|
||||
int Cflag;
|
||||
int Pflag;
|
||||
BlockAcctCookie acct;
|
||||
int pattern;
|
||||
struct timeval t1;
|
||||
};
|
||||
|
@ -1361,6 +1366,8 @@ static void aio_write_done(void *opaque, int ret)
|
|||
goto out;
|
||||
}
|
||||
|
||||
block_acct_done(blk_get_stats(ctx->blk), &ctx->acct);
|
||||
|
||||
if (ctx->qflag) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -1398,6 +1405,8 @@ static void aio_read_done(void *opaque, int ret)
|
|||
g_free(cmp_buf);
|
||||
}
|
||||
|
||||
block_acct_done(blk_get_stats(ctx->blk), &ctx->acct);
|
||||
|
||||
if (ctx->qflag) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -1436,7 +1445,7 @@ static void aio_read_help(void)
|
|||
"\n");
|
||||
}
|
||||
|
||||
static int aio_read_f(BlockDriverState *bs, int argc, char **argv);
|
||||
static int aio_read_f(BlockBackend *blk, int argc, char **argv);
|
||||
|
||||
static const cmdinfo_t aio_read_cmd = {
|
||||
.name = "aio_read",
|
||||
|
@ -1448,11 +1457,12 @@ static const cmdinfo_t aio_read_cmd = {
|
|||
.help = aio_read_help,
|
||||
};
|
||||
|
||||
static int aio_read_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int aio_read_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
int nr_iov, c;
|
||||
struct aio_ctx *ctx = g_new0(struct aio_ctx, 1);
|
||||
|
||||
ctx->blk = blk;
|
||||
while ((c = getopt(argc, argv, "CP:qv")) != EOF) {
|
||||
switch (c) {
|
||||
case 'C':
|
||||
|
@ -1499,15 +1509,17 @@ static int aio_read_f(BlockDriverState *bs, int argc, char **argv)
|
|||
}
|
||||
|
||||
nr_iov = argc - optind;
|
||||
ctx->buf = create_iovec(bs, &ctx->qiov, &argv[optind], nr_iov, 0xab);
|
||||
ctx->buf = create_iovec(blk, &ctx->qiov, &argv[optind], nr_iov, 0xab);
|
||||
if (ctx->buf == NULL) {
|
||||
g_free(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
gettimeofday(&ctx->t1, NULL);
|
||||
bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov,
|
||||
ctx->qiov.size >> 9, aio_read_done, ctx);
|
||||
block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size,
|
||||
BLOCK_ACCT_READ);
|
||||
blk_aio_readv(blk, ctx->offset >> 9, &ctx->qiov,
|
||||
ctx->qiov.size >> 9, aio_read_done, ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1531,7 +1543,7 @@ static void aio_write_help(void)
|
|||
"\n");
|
||||
}
|
||||
|
||||
static int aio_write_f(BlockDriverState *bs, int argc, char **argv);
|
||||
static int aio_write_f(BlockBackend *blk, int argc, char **argv);
|
||||
|
||||
static const cmdinfo_t aio_write_cmd = {
|
||||
.name = "aio_write",
|
||||
|
@ -1543,12 +1555,13 @@ static const cmdinfo_t aio_write_cmd = {
|
|||
.help = aio_write_help,
|
||||
};
|
||||
|
||||
static int aio_write_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int aio_write_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
int nr_iov, c;
|
||||
int pattern = 0xcd;
|
||||
struct aio_ctx *ctx = g_new0(struct aio_ctx, 1);
|
||||
|
||||
ctx->blk = blk;
|
||||
while ((c = getopt(argc, argv, "CqP:")) != EOF) {
|
||||
switch (c) {
|
||||
case 'C':
|
||||
|
@ -1591,21 +1604,23 @@ static int aio_write_f(BlockDriverState *bs, int argc, char **argv)
|
|||
}
|
||||
|
||||
nr_iov = argc - optind;
|
||||
ctx->buf = create_iovec(bs, &ctx->qiov, &argv[optind], nr_iov, pattern);
|
||||
ctx->buf = create_iovec(blk, &ctx->qiov, &argv[optind], nr_iov, pattern);
|
||||
if (ctx->buf == NULL) {
|
||||
g_free(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
gettimeofday(&ctx->t1, NULL);
|
||||
bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov,
|
||||
ctx->qiov.size >> 9, aio_write_done, ctx);
|
||||
block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size,
|
||||
BLOCK_ACCT_WRITE);
|
||||
blk_aio_writev(blk, ctx->offset >> 9, &ctx->qiov,
|
||||
ctx->qiov.size >> 9, aio_write_done, ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aio_flush_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int aio_flush_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
bdrv_drain_all();
|
||||
blk_drain_all();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1615,9 +1630,9 @@ static const cmdinfo_t aio_flush_cmd = {
|
|||
.oneline = "completes all outstanding aio requests"
|
||||
};
|
||||
|
||||
static int flush_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int flush_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
bdrv_flush(bs);
|
||||
blk_flush(blk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1628,7 +1643,7 @@ static const cmdinfo_t flush_cmd = {
|
|||
.oneline = "flush all in-core file state to disk",
|
||||
};
|
||||
|
||||
static int truncate_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int truncate_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
int64_t offset;
|
||||
int ret;
|
||||
|
@ -1639,7 +1654,7 @@ static int truncate_f(BlockDriverState *bs, int argc, char **argv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
ret = bdrv_truncate(bs, offset);
|
||||
ret = blk_truncate(blk, offset);
|
||||
if (ret < 0) {
|
||||
printf("truncate: %s\n", strerror(-ret));
|
||||
return 0;
|
||||
|
@ -1658,12 +1673,12 @@ static const cmdinfo_t truncate_cmd = {
|
|||
.oneline = "truncates the current file at the given offset",
|
||||
};
|
||||
|
||||
static int length_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int length_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
int64_t size;
|
||||
char s1[64];
|
||||
|
||||
size = bdrv_getlength(bs);
|
||||
size = blk_getlength(blk);
|
||||
if (size < 0) {
|
||||
printf("getlength: %s\n", strerror(-size));
|
||||
return 0;
|
||||
|
@ -1683,8 +1698,9 @@ static const cmdinfo_t length_cmd = {
|
|||
};
|
||||
|
||||
|
||||
static int info_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int info_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
BlockDriverInfo bdi;
|
||||
ImageInfoSpecific *spec_info;
|
||||
char s1[64], s2[64];
|
||||
|
@ -1742,7 +1758,7 @@ static void discard_help(void)
|
|||
"\n");
|
||||
}
|
||||
|
||||
static int discard_f(BlockDriverState *bs, int argc, char **argv);
|
||||
static int discard_f(BlockBackend *blk, int argc, char **argv);
|
||||
|
||||
static const cmdinfo_t discard_cmd = {
|
||||
.name = "discard",
|
||||
|
@ -1755,7 +1771,7 @@ static const cmdinfo_t discard_cmd = {
|
|||
.help = discard_help,
|
||||
};
|
||||
|
||||
static int discard_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int discard_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
struct timeval t1, t2;
|
||||
int Cflag = 0, qflag = 0;
|
||||
|
@ -1794,8 +1810,8 @@ static int discard_f(BlockDriverState *bs, int argc, char **argv)
|
|||
}
|
||||
|
||||
gettimeofday(&t1, NULL);
|
||||
ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS,
|
||||
count >> BDRV_SECTOR_BITS);
|
||||
ret = blk_discard(blk, offset >> BDRV_SECTOR_BITS,
|
||||
count >> BDRV_SECTOR_BITS);
|
||||
gettimeofday(&t2, NULL);
|
||||
|
||||
if (ret < 0) {
|
||||
|
@ -1813,8 +1829,9 @@ out:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int alloc_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int alloc_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
int64_t offset, sector_num;
|
||||
int nb_sectors, remaining;
|
||||
char s1[64];
|
||||
|
@ -1910,20 +1927,27 @@ static int map_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
|||
return firstret;
|
||||
}
|
||||
|
||||
static int map_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int map_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
int64_t offset;
|
||||
int64_t nb_sectors;
|
||||
int64_t nb_sectors, total_sectors;
|
||||
char s1[64];
|
||||
int64_t num;
|
||||
int ret;
|
||||
const char *retstr;
|
||||
|
||||
offset = 0;
|
||||
nb_sectors = bs->total_sectors;
|
||||
total_sectors = blk_nb_sectors(blk);
|
||||
if (total_sectors < 0) {
|
||||
error_report("Failed to query image length: %s",
|
||||
strerror(-total_sectors));
|
||||
return 0;
|
||||
}
|
||||
|
||||
nb_sectors = total_sectors;
|
||||
|
||||
do {
|
||||
ret = map_is_allocated(bs, offset, nb_sectors, &num);
|
||||
ret = map_is_allocated(blk_bs(blk), offset, nb_sectors, &num);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to get allocation status: %s", strerror(-ret));
|
||||
return 0;
|
||||
|
@ -1940,7 +1964,7 @@ static int map_f(BlockDriverState *bs, int argc, char **argv)
|
|||
|
||||
offset += num;
|
||||
nb_sectors -= num;
|
||||
} while (offset < bs->total_sectors);
|
||||
} while (offset < total_sectors);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1954,11 +1978,11 @@ static const cmdinfo_t map_cmd = {
|
|||
.oneline = "prints the allocated areas of a file",
|
||||
};
|
||||
|
||||
static int break_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int break_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bdrv_debug_breakpoint(bs, argv[1], argv[2]);
|
||||
ret = bdrv_debug_breakpoint(blk_bs(blk), argv[1], argv[2]);
|
||||
if (ret < 0) {
|
||||
printf("Could not set breakpoint: %s\n", strerror(-ret));
|
||||
}
|
||||
|
@ -1966,11 +1990,11 @@ static int break_f(BlockDriverState *bs, int argc, char **argv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int remove_break_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int remove_break_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bdrv_debug_remove_breakpoint(bs, argv[1]);
|
||||
ret = bdrv_debug_remove_breakpoint(blk_bs(blk), argv[1]);
|
||||
if (ret < 0) {
|
||||
printf("Could not remove breakpoint %s: %s\n", argv[1], strerror(-ret));
|
||||
}
|
||||
|
@ -1997,11 +2021,11 @@ static const cmdinfo_t remove_break_cmd = {
|
|||
.oneline = "remove a breakpoint by tag",
|
||||
};
|
||||
|
||||
static int resume_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int resume_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bdrv_debug_resume(bs, argv[1]);
|
||||
ret = bdrv_debug_resume(blk_bs(blk), argv[1]);
|
||||
if (ret < 0) {
|
||||
printf("Could not resume request: %s\n", strerror(-ret));
|
||||
}
|
||||
|
@ -2018,10 +2042,10 @@ static const cmdinfo_t resume_cmd = {
|
|||
.oneline = "resumes the request tagged as tag",
|
||||
};
|
||||
|
||||
static int wait_break_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int wait_break_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
while (!bdrv_debug_is_suspended(bs, argv[1])) {
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
while (!bdrv_debug_is_suspended(blk_bs(blk), argv[1])) {
|
||||
aio_poll(blk_get_aio_context(blk), true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -2036,7 +2060,7 @@ static const cmdinfo_t wait_break_cmd = {
|
|||
.oneline = "waits for the suspension of a request",
|
||||
};
|
||||
|
||||
static int abort_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int abort_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
@ -2062,7 +2086,7 @@ static void sigraise_help(void)
|
|||
"\n", SIGTERM);
|
||||
}
|
||||
|
||||
static int sigraise_f(BlockDriverState *bs, int argc, char **argv);
|
||||
static int sigraise_f(BlockBackend *blk, int argc, char **argv);
|
||||
|
||||
static const cmdinfo_t sigraise_cmd = {
|
||||
.name = "sigraise",
|
||||
|
@ -2075,7 +2099,7 @@ static const cmdinfo_t sigraise_cmd = {
|
|||
.help = sigraise_help,
|
||||
};
|
||||
|
||||
static int sigraise_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int sigraise_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
int sig = cvtnum(argv[1]);
|
||||
if (sig < 0) {
|
||||
|
@ -2099,7 +2123,7 @@ static void sleep_cb(void *opaque)
|
|||
*expired = true;
|
||||
}
|
||||
|
||||
static int sleep_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int sleep_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
char *endptr;
|
||||
long ms;
|
||||
|
@ -2168,7 +2192,7 @@ static void help_all(void)
|
|||
printf("\nUse 'help commandname' for extended help.\n");
|
||||
}
|
||||
|
||||
static int help_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int help_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
const cmdinfo_t *ct;
|
||||
|
||||
|
@ -2198,7 +2222,7 @@ static const cmdinfo_t help_cmd = {
|
|||
.oneline = "help for one or all commands",
|
||||
};
|
||||
|
||||
bool qemuio_command(BlockDriverState *bs, const char *cmd)
|
||||
bool qemuio_command(BlockBackend *blk, const char *cmd)
|
||||
{
|
||||
char *input;
|
||||
const cmdinfo_t *ct;
|
||||
|
@ -2211,7 +2235,7 @@ bool qemuio_command(BlockDriverState *bs, const char *cmd)
|
|||
if (c) {
|
||||
ct = find_command(v[0]);
|
||||
if (ct) {
|
||||
done = command(bs, ct, c, v);
|
||||
done = command(blk, ct, c, v);
|
||||
} else {
|
||||
fprintf(stderr, "command \"%s\" not found\n", v[0]);
|
||||
}
|
||||
|
|
58
qemu-io.c
58
qemu-io.c
|
@ -28,7 +28,6 @@
|
|||
static char *progname;
|
||||
|
||||
static BlockBackend *qemuio_blk;
|
||||
static BlockDriverState *qemuio_bs;
|
||||
|
||||
/* qemu-io commands passed using -c */
|
||||
static int ncmdline;
|
||||
|
@ -36,10 +35,9 @@ static char **cmdline;
|
|||
|
||||
static ReadLineState *readline_state;
|
||||
|
||||
static int close_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int close_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
blk_unref(qemuio_blk);
|
||||
qemuio_bs = NULL;
|
||||
qemuio_blk = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
@ -51,32 +49,22 @@ static const cmdinfo_t close_cmd = {
|
|||
.oneline = "close the current open file",
|
||||
};
|
||||
|
||||
static int openfile(char *name, BlockDriver *drv, int flags, int growable,
|
||||
QDict *opts)
|
||||
static int openfile(char *name, int flags, QDict *opts)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (qemuio_bs) {
|
||||
if (qemuio_blk) {
|
||||
fprintf(stderr, "file open already, try 'help close'\n");
|
||||
QDECREF(opts);
|
||||
return 1;
|
||||
}
|
||||
|
||||
qemuio_blk = blk_new_with_bs("hda", &error_abort);
|
||||
qemuio_bs = blk_bs(qemuio_blk);
|
||||
|
||||
if (growable) {
|
||||
flags |= BDRV_O_PROTOCOL;
|
||||
}
|
||||
|
||||
if (bdrv_open(&qemuio_bs, name, NULL, opts, flags, drv, &local_err) < 0) {
|
||||
qemuio_blk = blk_new_open("hda", name, NULL, opts, flags, &local_err);
|
||||
if (!qemuio_blk) {
|
||||
fprintf(stderr, "%s: can't open%s%s: %s\n", progname,
|
||||
name ? " device " : "", name ?: "",
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
blk_unref(qemuio_blk);
|
||||
qemuio_bs = NULL;
|
||||
qemuio_blk = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -96,12 +84,11 @@ static void open_help(void)
|
|||
" -r, -- open file read-only\n"
|
||||
" -s, -- use snapshot file\n"
|
||||
" -n, -- disable host cache\n"
|
||||
" -g, -- allow file to grow (only applies to protocols)\n"
|
||||
" -o, -- options to be given to the block driver"
|
||||
"\n");
|
||||
}
|
||||
|
||||
static int open_f(BlockDriverState *bs, int argc, char **argv);
|
||||
static int open_f(BlockBackend *blk, int argc, char **argv);
|
||||
|
||||
static const cmdinfo_t open_cmd = {
|
||||
.name = "open",
|
||||
|
@ -125,11 +112,10 @@ static QemuOptsList empty_opts = {
|
|||
},
|
||||
};
|
||||
|
||||
static int open_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int open_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
int flags = 0;
|
||||
int readonly = 0;
|
||||
int growable = 0;
|
||||
int c;
|
||||
QemuOpts *qopts;
|
||||
QDict *opts;
|
||||
|
@ -145,9 +131,6 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
|
|||
case 'r':
|
||||
readonly = 1;
|
||||
break;
|
||||
case 'g':
|
||||
growable = 1;
|
||||
break;
|
||||
case 'o':
|
||||
if (!qemu_opts_parse(&empty_opts, optarg, 0)) {
|
||||
printf("could not parse option list -- %s\n", optarg);
|
||||
|
@ -170,16 +153,16 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
|
|||
qemu_opts_reset(&empty_opts);
|
||||
|
||||
if (optind == argc - 1) {
|
||||
return openfile(argv[optind], NULL, flags, growable, opts);
|
||||
return openfile(argv[optind], flags, opts);
|
||||
} else if (optind == argc) {
|
||||
return openfile(NULL, NULL, flags, growable, opts);
|
||||
return openfile(NULL, flags, opts);
|
||||
} else {
|
||||
QDECREF(opts);
|
||||
return qemuio_command_usage(&open_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static int quit_f(BlockDriverState *bs, int argc, char **argv)
|
||||
static int quit_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
@ -206,7 +189,6 @@ static void usage(const char *name)
|
|||
" -r, --read-only export read-only\n"
|
||||
" -s, --snapshot use snapshot file\n"
|
||||
" -n, --nocache disable host cache\n"
|
||||
" -g, --growable allow file to grow (only applies to protocols)\n"
|
||||
" -m, --misalign misalign allocations for O_DIRECT\n"
|
||||
" -k, --native-aio use kernel AIO implementation (on Linux only)\n"
|
||||
" -t, --cache=MODE use the given cache mode for the image\n"
|
||||
|
@ -317,7 +299,7 @@ static void command_loop(void)
|
|||
char *input;
|
||||
|
||||
for (i = 0; !done && i < ncmdline; i++) {
|
||||
done = qemuio_command(qemuio_bs, cmdline[i]);
|
||||
done = qemuio_command(qemuio_blk, cmdline[i]);
|
||||
}
|
||||
if (cmdline) {
|
||||
g_free(cmdline);
|
||||
|
@ -342,7 +324,7 @@ static void command_loop(void)
|
|||
if (input == NULL) {
|
||||
break;
|
||||
}
|
||||
done = qemuio_command(qemuio_bs, input);
|
||||
done = qemuio_command(qemuio_blk, input);
|
||||
g_free(input);
|
||||
|
||||
prompted = 0;
|
||||
|
@ -365,7 +347,6 @@ static void reenable_tty_echo(void)
|
|||
int main(int argc, char **argv)
|
||||
{
|
||||
int readonly = 0;
|
||||
int growable = 0;
|
||||
const char *sopt = "hVc:d:f:rsnmgkt:T:";
|
||||
const struct option lopt[] = {
|
||||
{ "help", 0, NULL, 'h' },
|
||||
|
@ -377,7 +358,6 @@ int main(int argc, char **argv)
|
|||
{ "snapshot", 0, NULL, 's' },
|
||||
{ "nocache", 0, NULL, 'n' },
|
||||
{ "misalign", 0, NULL, 'm' },
|
||||
{ "growable", 0, NULL, 'g' },
|
||||
{ "native-aio", 0, NULL, 'k' },
|
||||
{ "discard", 1, NULL, 'd' },
|
||||
{ "cache", 1, NULL, 't' },
|
||||
|
@ -387,8 +367,8 @@ int main(int argc, char **argv)
|
|||
int c;
|
||||
int opt_index = 0;
|
||||
int flags = BDRV_O_UNMAP;
|
||||
BlockDriver *drv = NULL;
|
||||
Error *local_error = NULL;
|
||||
QDict *opts = NULL;
|
||||
|
||||
#ifdef CONFIG_POSIX
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
@ -414,11 +394,10 @@ int main(int argc, char **argv)
|
|||
}
|
||||
break;
|
||||
case 'f':
|
||||
drv = bdrv_find_format(optarg);
|
||||
if (!drv) {
|
||||
error_report("Invalid format '%s'", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
if (!opts) {
|
||||
opts = qdict_new();
|
||||
}
|
||||
qdict_put(opts, "driver", qstring_from_str(optarg));
|
||||
break;
|
||||
case 'c':
|
||||
add_user_command(optarg);
|
||||
|
@ -429,9 +408,6 @@ int main(int argc, char **argv)
|
|||
case 'm':
|
||||
qemuio_misalign = true;
|
||||
break;
|
||||
case 'g':
|
||||
growable = 1;
|
||||
break;
|
||||
case 'k':
|
||||
flags |= BDRV_O_NATIVE_AIO;
|
||||
break;
|
||||
|
@ -489,7 +465,7 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
if ((argc - optind) == 1) {
|
||||
openfile(argv[optind], drv, flags, growable, NULL);
|
||||
openfile(argv[optind], flags, opts);
|
||||
}
|
||||
command_loop();
|
||||
|
||||
|
|
25
qemu-nbd.c
25
qemu-nbd.c
|
@ -391,7 +391,6 @@ int main(int argc, char **argv)
|
|||
{
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
BlockDriver *drv;
|
||||
off_t dev_offset = 0;
|
||||
uint32_t nbdflags = 0;
|
||||
bool disconnect = false;
|
||||
|
@ -434,7 +433,7 @@ int main(int argc, char **argv)
|
|||
char *end;
|
||||
int flags = BDRV_O_RDWR;
|
||||
int partition = -1;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
int fd;
|
||||
bool seen_cache = false;
|
||||
bool seen_discard = false;
|
||||
|
@ -445,6 +444,7 @@ int main(int argc, char **argv)
|
|||
const char *fmt = NULL;
|
||||
Error *local_err = NULL;
|
||||
BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
|
||||
QDict *options = NULL;
|
||||
|
||||
/* The client thread uses SIGTERM to interrupt the server. A signal
|
||||
* handler ensures that "qemu-nbd -v -c" exits with a nice status code.
|
||||
|
@ -689,24 +689,17 @@ int main(int argc, char **argv)
|
|||
atexit(bdrv_close_all);
|
||||
|
||||
if (fmt) {
|
||||
drv = bdrv_find_format(fmt);
|
||||
if (!drv) {
|
||||
errx(EXIT_FAILURE, "Unknown file format '%s'", fmt);
|
||||
}
|
||||
} else {
|
||||
drv = NULL;
|
||||
options = qdict_new();
|
||||
qdict_put(options, "driver", qstring_from_str(fmt));
|
||||
}
|
||||
|
||||
blk = blk_new_with_bs("hda", &error_abort);
|
||||
bs = blk_bs(blk);
|
||||
|
||||
srcpath = argv[optind];
|
||||
ret = bdrv_open(&bs, srcpath, NULL, NULL, flags, drv, &local_err);
|
||||
if (ret < 0) {
|
||||
errno = -ret;
|
||||
err(EXIT_FAILURE, "Failed to bdrv_open '%s': %s", argv[optind],
|
||||
error_get_pretty(local_err));
|
||||
blk = blk_new_open("hda", srcpath, NULL, options, flags, &local_err);
|
||||
if (!blk) {
|
||||
errx(EXIT_FAILURE, "Failed to blk_new_open '%s': %s", argv[optind],
|
||||
error_get_pretty(local_err));
|
||||
}
|
||||
bs = blk_bs(blk);
|
||||
|
||||
if (sn_opts) {
|
||||
ret = bdrv_snapshot_load_tmp(bs,
|
||||
|
|
11
savevm.c
11
savevm.c
|
@ -821,7 +821,7 @@ void qemu_savevm_state_cancel(void)
|
|||
}
|
||||
}
|
||||
|
||||
static int qemu_savevm_state(QEMUFile *f)
|
||||
static int qemu_savevm_state(QEMUFile *f, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
MigrationParams params = {
|
||||
|
@ -829,7 +829,7 @@ static int qemu_savevm_state(QEMUFile *f)
|
|||
.shared = 0
|
||||
};
|
||||
|
||||
if (qemu_savevm_state_blocked(NULL)) {
|
||||
if (qemu_savevm_state_blocked(errp)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -850,6 +850,7 @@ static int qemu_savevm_state(QEMUFile *f)
|
|||
}
|
||||
if (ret != 0) {
|
||||
qemu_savevm_state_cancel();
|
||||
error_setg_errno(errp, -ret, "Error while writing VM state");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -1102,6 +1103,7 @@ void do_savevm(Monitor *mon, const QDict *qdict)
|
|||
qemu_timeval tv;
|
||||
struct tm tm;
|
||||
const char *name = qdict_get_try_str(qdict, "name");
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* Verify if there is a device that doesn't support snapshots and is writable */
|
||||
bs = NULL;
|
||||
|
@ -1160,11 +1162,12 @@ void do_savevm(Monitor *mon, const QDict *qdict)
|
|||
monitor_printf(mon, "Could not open VM state file\n");
|
||||
goto the_end;
|
||||
}
|
||||
ret = qemu_savevm_state(f);
|
||||
ret = qemu_savevm_state(f, &local_err);
|
||||
vm_state_size = qemu_ftell(f);
|
||||
qemu_fclose(f);
|
||||
if (ret < 0) {
|
||||
monitor_printf(mon, "Error %d while writing VM\n", ret);
|
||||
monitor_printf(mon, "%s\n", error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
goto the_end;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
# QEMU qtest library
|
||||
#
|
||||
# Copyright (C) 2015 Red Hat Inc.
|
||||
#
|
||||
# Authors:
|
||||
# Fam Zheng <famz@redhat.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
# the COPYING file in the top-level directory.
|
||||
#
|
||||
# Based on qmp.py.
|
||||
#
|
||||
|
||||
import errno
|
||||
import socket
|
||||
|
||||
class QEMUQtestProtocol(object):
|
||||
def __init__(self, address, server=False):
|
||||
"""
|
||||
Create a QEMUQtestProtocol object.
|
||||
|
||||
@param address: QEMU address, can be either a unix socket path (string)
|
||||
or a tuple in the form ( address, port ) for a TCP
|
||||
connection
|
||||
@param server: server mode, listens on the socket (bool)
|
||||
@raise socket.error on socket connection errors
|
||||
@note No connection is established, this is done by the connect() or
|
||||
accept() methods
|
||||
"""
|
||||
self._address = address
|
||||
self._sock = self._get_sock()
|
||||
if server:
|
||||
self._sock.bind(self._address)
|
||||
self._sock.listen(1)
|
||||
|
||||
def _get_sock(self):
|
||||
if isinstance(self._address, tuple):
|
||||
family = socket.AF_INET
|
||||
else:
|
||||
family = socket.AF_UNIX
|
||||
return socket.socket(family, socket.SOCK_STREAM)
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
Connect to the qtest socket.
|
||||
|
||||
@raise socket.error on socket connection errors
|
||||
"""
|
||||
self._sock.connect(self._address)
|
||||
|
||||
def accept(self):
|
||||
"""
|
||||
Await connection from QEMU.
|
||||
|
||||
@raise socket.error on socket connection errors
|
||||
"""
|
||||
self._sock, _ = self._sock.accept()
|
||||
|
||||
def cmd(self, qtest_cmd):
|
||||
"""
|
||||
Send a qtest command on the wire.
|
||||
|
||||
@param qtest_cmd: qtest command text to be sent
|
||||
"""
|
||||
self._sock.sendall(qtest_cmd + "\n")
|
||||
|
||||
def close(self):
|
||||
self._sock.close()
|
||||
|
||||
def settimeout(self, timeout):
|
||||
self._sock.settimeout(timeout)
|
|
@ -307,9 +307,10 @@ tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a
|
|||
tests/test-bitops$(EXESUF): tests/test-bitops.o libqemuutil.a
|
||||
|
||||
libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o
|
||||
libqos-obj-y += tests/libqos/i2c.o
|
||||
libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
|
||||
libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o
|
||||
libqos-pc-obj-y += tests/libqos/malloc-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
|
||||
|
|
1072
tests/ahci-test.c
1072
tests/ahci-test.c
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,838 @@
|
|||
/*
|
||||
* libqos AHCI functions
|
||||
*
|
||||
* Copyright (c) 2014 John Snow <jsnow@redhat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "libqtest.h"
|
||||
#include "libqos/ahci.h"
|
||||
#include "libqos/pci-pc.h"
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/host-utils.h"
|
||||
|
||||
#include "hw/pci/pci_ids.h"
|
||||
#include "hw/pci/pci_regs.h"
|
||||
|
||||
typedef struct AHCICommandProp {
|
||||
uint8_t cmd; /* Command Code */
|
||||
bool data; /* Data transfer command? */
|
||||
bool pio;
|
||||
bool dma;
|
||||
bool lba28;
|
||||
bool lba48;
|
||||
bool read;
|
||||
bool write;
|
||||
bool atapi;
|
||||
bool ncq;
|
||||
uint64_t size; /* Static transfer size, for commands like IDENTIFY. */
|
||||
uint32_t interrupts; /* Expected interrupts for this command. */
|
||||
} AHCICommandProp;
|
||||
|
||||
AHCICommandProp ahci_command_properties[] = {
|
||||
{ .cmd = CMD_READ_PIO, .data = true, .pio = true,
|
||||
.lba28 = true, .read = true },
|
||||
{ .cmd = CMD_WRITE_PIO, .data = true, .pio = true,
|
||||
.lba28 = true, .write = true },
|
||||
{ .cmd = CMD_READ_PIO_EXT, .data = true, .pio = true,
|
||||
.lba48 = true, .read = true },
|
||||
{ .cmd = CMD_WRITE_PIO_EXT, .data = true, .pio = true,
|
||||
.lba48 = true, .write = true },
|
||||
{ .cmd = CMD_READ_DMA, .data = true, .dma = true,
|
||||
.lba28 = true, .read = true },
|
||||
{ .cmd = CMD_WRITE_DMA, .data = true, .dma = true,
|
||||
.lba28 = true, .write = true },
|
||||
{ .cmd = CMD_READ_DMA_EXT, .data = true, .dma = true,
|
||||
.lba48 = true, .read = true },
|
||||
{ .cmd = CMD_WRITE_DMA_EXT, .data = true, .dma = true,
|
||||
.lba48 = true, .write = true },
|
||||
{ .cmd = CMD_IDENTIFY, .data = true, .pio = true,
|
||||
.size = 512, .read = true },
|
||||
{ .cmd = CMD_READ_MAX, .lba28 = true },
|
||||
{ .cmd = CMD_READ_MAX_EXT, .lba48 = true },
|
||||
{ .cmd = CMD_FLUSH_CACHE, .data = false }
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocate space in the guest using information in the AHCIQState object.
|
||||
*/
|
||||
uint64_t ahci_alloc(AHCIQState *ahci, size_t bytes)
|
||||
{
|
||||
g_assert(ahci);
|
||||
g_assert(ahci->parent);
|
||||
return qmalloc(ahci->parent, bytes);
|
||||
}
|
||||
|
||||
void ahci_free(AHCIQState *ahci, uint64_t addr)
|
||||
{
|
||||
g_assert(ahci);
|
||||
g_assert(ahci->parent);
|
||||
qfree(ahci->parent, addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate, verify, and return a handle to the AHCI device.
|
||||
*/
|
||||
QPCIDevice *get_ahci_device(uint32_t *fingerprint)
|
||||
{
|
||||
QPCIDevice *ahci;
|
||||
uint32_t ahci_fingerprint;
|
||||
QPCIBus *pcibus;
|
||||
|
||||
pcibus = qpci_init_pc();
|
||||
|
||||
/* Find the AHCI PCI device and verify it's the right one. */
|
||||
ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02));
|
||||
g_assert(ahci != NULL);
|
||||
|
||||
ahci_fingerprint = qpci_config_readl(ahci, PCI_VENDOR_ID);
|
||||
|
||||
switch (ahci_fingerprint) {
|
||||
case AHCI_INTEL_ICH9:
|
||||
break;
|
||||
default:
|
||||
/* Unknown device. */
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
if (fingerprint) {
|
||||
*fingerprint = ahci_fingerprint;
|
||||
}
|
||||
return ahci;
|
||||
}
|
||||
|
||||
void free_ahci_device(QPCIDevice *dev)
|
||||
{
|
||||
QPCIBus *pcibus = dev ? dev->bus : NULL;
|
||||
|
||||
/* libqos doesn't have a function for this, so free it manually */
|
||||
g_free(dev);
|
||||
qpci_free_pc(pcibus);
|
||||
}
|
||||
|
||||
/* Free all memory in-use by the AHCI device. */
|
||||
void ahci_clean_mem(AHCIQState *ahci)
|
||||
{
|
||||
uint8_t port, slot;
|
||||
|
||||
for (port = 0; port < 32; ++port) {
|
||||
if (ahci->port[port].fb) {
|
||||
ahci_free(ahci, ahci->port[port].fb);
|
||||
}
|
||||
if (ahci->port[port].clb) {
|
||||
for (slot = 0; slot < 32; slot++) {
|
||||
ahci_destroy_command(ahci, port, slot);
|
||||
}
|
||||
ahci_free(ahci, ahci->port[port].clb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*** Logical Device Initialization ***/
|
||||
|
||||
/**
|
||||
* Start the PCI device and sanity-check default operation.
|
||||
*/
|
||||
void ahci_pci_enable(AHCIQState *ahci)
|
||||
{
|
||||
uint8_t reg;
|
||||
|
||||
start_ahci_device(ahci);
|
||||
|
||||
switch (ahci->fingerprint) {
|
||||
case AHCI_INTEL_ICH9:
|
||||
/* ICH9 has a register at PCI 0x92 that
|
||||
* acts as a master port enabler mask. */
|
||||
reg = qpci_config_readb(ahci->dev, 0x92);
|
||||
reg |= 0x3F;
|
||||
qpci_config_writeb(ahci->dev, 0x92, reg);
|
||||
/* 0...0111111b -- bit significant, ports 0-5 enabled. */
|
||||
ASSERT_BIT_SET(qpci_config_readb(ahci->dev, 0x92), 0x3F);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Map BAR5/ABAR, and engage the PCI device.
|
||||
*/
|
||||
void start_ahci_device(AHCIQState *ahci)
|
||||
{
|
||||
/* Map AHCI's ABAR (BAR5) */
|
||||
ahci->hba_base = qpci_iomap(ahci->dev, 5, &ahci->barsize);
|
||||
g_assert(ahci->hba_base);
|
||||
|
||||
/* turns on pci.cmd.iose, pci.cmd.mse and pci.cmd.bme */
|
||||
qpci_device_enable(ahci->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test and initialize the AHCI's HBA memory areas.
|
||||
* Initialize and start any ports with devices attached.
|
||||
* Bring the HBA into the idle state.
|
||||
*/
|
||||
void ahci_hba_enable(AHCIQState *ahci)
|
||||
{
|
||||
/* Bits of interest in this section:
|
||||
* GHC.AE Global Host Control / AHCI Enable
|
||||
* PxCMD.ST Port Command: Start
|
||||
* PxCMD.SUD "Spin Up Device"
|
||||
* PxCMD.POD "Power On Device"
|
||||
* PxCMD.FRE "FIS Receive Enable"
|
||||
* PxCMD.FR "FIS Receive Running"
|
||||
* PxCMD.CR "Command List Running"
|
||||
*/
|
||||
uint32_t reg, ports_impl;
|
||||
uint16_t i;
|
||||
uint8_t num_cmd_slots;
|
||||
|
||||
g_assert(ahci != NULL);
|
||||
|
||||
/* Set GHC.AE to 1 */
|
||||
ahci_set(ahci, AHCI_GHC, AHCI_GHC_AE);
|
||||
reg = ahci_rreg(ahci, AHCI_GHC);
|
||||
ASSERT_BIT_SET(reg, AHCI_GHC_AE);
|
||||
|
||||
/* Cache CAP and CAP2. */
|
||||
ahci->cap = ahci_rreg(ahci, AHCI_CAP);
|
||||
ahci->cap2 = ahci_rreg(ahci, AHCI_CAP2);
|
||||
|
||||
/* Read CAP.NCS, how many command slots do we have? */
|
||||
num_cmd_slots = ((ahci->cap & AHCI_CAP_NCS) >> ctzl(AHCI_CAP_NCS)) + 1;
|
||||
g_test_message("Number of Command Slots: %u", num_cmd_slots);
|
||||
|
||||
/* Determine which ports are implemented. */
|
||||
ports_impl = ahci_rreg(ahci, AHCI_PI);
|
||||
|
||||
for (i = 0; ports_impl; ports_impl >>= 1, ++i) {
|
||||
if (!(ports_impl & 0x01)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
g_test_message("Initializing port %u", i);
|
||||
|
||||
reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD);
|
||||
if (BITCLR(reg, AHCI_PX_CMD_ST | AHCI_PX_CMD_CR |
|
||||
AHCI_PX_CMD_FRE | AHCI_PX_CMD_FR)) {
|
||||
g_test_message("port is idle");
|
||||
} else {
|
||||
g_test_message("port needs to be idled");
|
||||
ahci_px_clr(ahci, i, AHCI_PX_CMD,
|
||||
(AHCI_PX_CMD_ST | AHCI_PX_CMD_FRE));
|
||||
/* The port has 500ms to disengage. */
|
||||
usleep(500000);
|
||||
reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD);
|
||||
ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CR);
|
||||
ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FR);
|
||||
g_test_message("port is now idle");
|
||||
/* The spec does allow for possibly needing a PORT RESET
|
||||
* or HBA reset if we fail to idle the port. */
|
||||
}
|
||||
|
||||
/* Allocate Memory for the Command List Buffer & FIS Buffer */
|
||||
/* PxCLB space ... 0x20 per command, as in 4.2.2 p 36 */
|
||||
ahci->port[i].clb = ahci_alloc(ahci, num_cmd_slots * 0x20);
|
||||
qmemset(ahci->port[i].clb, 0x00, 0x100);
|
||||
g_test_message("CLB: 0x%08" PRIx64, ahci->port[i].clb);
|
||||
ahci_px_wreg(ahci, i, AHCI_PX_CLB, ahci->port[i].clb);
|
||||
g_assert_cmphex(ahci->port[i].clb, ==,
|
||||
ahci_px_rreg(ahci, i, AHCI_PX_CLB));
|
||||
|
||||
/* PxFB space ... 0x100, as in 4.2.1 p 35 */
|
||||
ahci->port[i].fb = ahci_alloc(ahci, 0x100);
|
||||
qmemset(ahci->port[i].fb, 0x00, 0x100);
|
||||
g_test_message("FB: 0x%08" PRIx64, ahci->port[i].fb);
|
||||
ahci_px_wreg(ahci, i, AHCI_PX_FB, ahci->port[i].fb);
|
||||
g_assert_cmphex(ahci->port[i].fb, ==,
|
||||
ahci_px_rreg(ahci, i, AHCI_PX_FB));
|
||||
|
||||
/* Clear PxSERR, PxIS, then IS.IPS[x] by writing '1's. */
|
||||
ahci_px_wreg(ahci, i, AHCI_PX_SERR, 0xFFFFFFFF);
|
||||
ahci_px_wreg(ahci, i, AHCI_PX_IS, 0xFFFFFFFF);
|
||||
ahci_wreg(ahci, AHCI_IS, (1 << i));
|
||||
|
||||
/* Verify Interrupts Cleared */
|
||||
reg = ahci_px_rreg(ahci, i, AHCI_PX_SERR);
|
||||
g_assert_cmphex(reg, ==, 0);
|
||||
|
||||
reg = ahci_px_rreg(ahci, i, AHCI_PX_IS);
|
||||
g_assert_cmphex(reg, ==, 0);
|
||||
|
||||
reg = ahci_rreg(ahci, AHCI_IS);
|
||||
ASSERT_BIT_CLEAR(reg, (1 << i));
|
||||
|
||||
/* Enable All Interrupts: */
|
||||
ahci_px_wreg(ahci, i, AHCI_PX_IE, 0xFFFFFFFF);
|
||||
reg = ahci_px_rreg(ahci, i, AHCI_PX_IE);
|
||||
g_assert_cmphex(reg, ==, ~((uint32_t)AHCI_PX_IE_RESERVED));
|
||||
|
||||
/* Enable the FIS Receive Engine. */
|
||||
ahci_px_set(ahci, i, AHCI_PX_CMD, AHCI_PX_CMD_FRE);
|
||||
reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD);
|
||||
ASSERT_BIT_SET(reg, AHCI_PX_CMD_FR);
|
||||
|
||||
/* AHCI 1.3 spec: if !STS.BSY, !STS.DRQ and PxSSTS.DET indicates
|
||||
* physical presence, a device is present and may be started. However,
|
||||
* PxSERR.DIAG.X /may/ need to be cleared a priori. */
|
||||
reg = ahci_px_rreg(ahci, i, AHCI_PX_SERR);
|
||||
if (BITSET(reg, AHCI_PX_SERR_DIAG_X)) {
|
||||
ahci_px_set(ahci, i, AHCI_PX_SERR, AHCI_PX_SERR_DIAG_X);
|
||||
}
|
||||
|
||||
reg = ahci_px_rreg(ahci, i, AHCI_PX_TFD);
|
||||
if (BITCLR(reg, AHCI_PX_TFD_STS_BSY | AHCI_PX_TFD_STS_DRQ)) {
|
||||
reg = ahci_px_rreg(ahci, i, AHCI_PX_SSTS);
|
||||
if ((reg & AHCI_PX_SSTS_DET) == SSTS_DET_ESTABLISHED) {
|
||||
/* Device Found: set PxCMD.ST := 1 */
|
||||
ahci_px_set(ahci, i, AHCI_PX_CMD, AHCI_PX_CMD_ST);
|
||||
ASSERT_BIT_SET(ahci_px_rreg(ahci, i, AHCI_PX_CMD),
|
||||
AHCI_PX_CMD_CR);
|
||||
g_test_message("Started Device %u", i);
|
||||
} else if ((reg & AHCI_PX_SSTS_DET)) {
|
||||
/* Device present, but in some unknown state. */
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable GHC.IE */
|
||||
ahci_set(ahci, AHCI_GHC, AHCI_GHC_IE);
|
||||
reg = ahci_rreg(ahci, AHCI_GHC);
|
||||
ASSERT_BIT_SET(reg, AHCI_GHC_IE);
|
||||
|
||||
/* TODO: The device should now be idling and waiting for commands.
|
||||
* In the future, a small test-case to inspect the Register D2H FIS
|
||||
* and clear the initial interrupts might be good. */
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick the first implemented and running port
|
||||
*/
|
||||
unsigned ahci_port_select(AHCIQState *ahci)
|
||||
{
|
||||
uint32_t ports, reg;
|
||||
unsigned i;
|
||||
|
||||
ports = ahci_rreg(ahci, AHCI_PI);
|
||||
for (i = 0; i < 32; ports >>= 1, ++i) {
|
||||
if (ports == 0) {
|
||||
i = 32;
|
||||
}
|
||||
|
||||
if (!(ports & 0x01)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD);
|
||||
if (BITSET(reg, AHCI_PX_CMD_ST)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_assert(i < 32);
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a port's interrupts and status information prior to a test.
|
||||
*/
|
||||
void ahci_port_clear(AHCIQState *ahci, uint8_t port)
|
||||
{
|
||||
uint32_t reg;
|
||||
|
||||
/* Clear out this port's interrupts (ignore the init register d2h fis) */
|
||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
|
||||
ahci_px_wreg(ahci, port, AHCI_PX_IS, reg);
|
||||
g_assert_cmphex(ahci_px_rreg(ahci, port, AHCI_PX_IS), ==, 0);
|
||||
|
||||
/* Wipe the FIS-Recieve Buffer */
|
||||
qmemset(ahci->port[port].fb, 0x00, 0x100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a port for errors.
|
||||
*/
|
||||
void ahci_port_check_error(AHCIQState *ahci, uint8_t port)
|
||||
{
|
||||
uint32_t reg;
|
||||
|
||||
/* The upper 9 bits of the IS register all indicate errors. */
|
||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
|
||||
reg >>= 23;
|
||||
g_assert_cmphex(reg, ==, 0);
|
||||
|
||||
/* The Sata Error Register should be empty. */
|
||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_SERR);
|
||||
g_assert_cmphex(reg, ==, 0);
|
||||
|
||||
/* The TFD also has two error sections. */
|
||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
|
||||
ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR);
|
||||
ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR);
|
||||
}
|
||||
|
||||
void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
|
||||
uint32_t intr_mask)
|
||||
{
|
||||
uint32_t reg;
|
||||
|
||||
/* Check for expected interrupts */
|
||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
|
||||
ASSERT_BIT_SET(reg, intr_mask);
|
||||
|
||||
/* Clear expected interrupts and assert all interrupts now cleared. */
|
||||
ahci_px_wreg(ahci, port, AHCI_PX_IS, intr_mask);
|
||||
g_assert_cmphex(ahci_px_rreg(ahci, port, AHCI_PX_IS), ==, 0);
|
||||
}
|
||||
|
||||
void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot)
|
||||
{
|
||||
uint32_t reg;
|
||||
|
||||
/* Assert that the command slot is no longer busy (NCQ) */
|
||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_SACT);
|
||||
ASSERT_BIT_CLEAR(reg, (1 << slot));
|
||||
|
||||
/* Non-NCQ */
|
||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_CI);
|
||||
ASSERT_BIT_CLEAR(reg, (1 << slot));
|
||||
|
||||
/* And assert that we are generally not busy. */
|
||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
|
||||
ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_BSY);
|
||||
ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_DRQ);
|
||||
}
|
||||
|
||||
void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot)
|
||||
{
|
||||
RegD2HFIS *d2h = g_malloc0(0x20);
|
||||
uint32_t reg;
|
||||
|
||||
memread(ahci->port[port].fb + 0x40, d2h, 0x20);
|
||||
g_assert_cmphex(d2h->fis_type, ==, 0x34);
|
||||
|
||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
|
||||
g_assert_cmphex((reg & AHCI_PX_TFD_ERR) >> 8, ==, d2h->error);
|
||||
g_assert_cmphex((reg & AHCI_PX_TFD_STS), ==, d2h->status);
|
||||
|
||||
g_free(d2h);
|
||||
}
|
||||
|
||||
void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t slot, size_t buffsize)
|
||||
{
|
||||
PIOSetupFIS *pio = g_malloc0(0x20);
|
||||
|
||||
/* We cannot check the Status or E_Status registers, becuase
|
||||
* the status may have again changed between the PIO Setup FIS
|
||||
* and the conclusion of the command with the D2H Register FIS. */
|
||||
memread(ahci->port[port].fb + 0x20, pio, 0x20);
|
||||
g_assert_cmphex(pio->fis_type, ==, 0x5f);
|
||||
|
||||
/* BUG: PIO Setup FIS as utilized by QEMU tries to fit the entire
|
||||
* transfer size in a uint16_t field. The maximum transfer size can
|
||||
* eclipse this; the field is meant to convey the size of data per
|
||||
* each Data FIS, not the entire operation as a whole. For now,
|
||||
* we will sanity check the broken case where applicable. */
|
||||
if (buffsize <= UINT16_MAX) {
|
||||
g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, buffsize);
|
||||
}
|
||||
|
||||
g_free(pio);
|
||||
}
|
||||
|
||||
void ahci_port_check_cmd_sanity(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t slot, size_t buffsize)
|
||||
{
|
||||
AHCICommandHeader cmd;
|
||||
|
||||
ahci_get_command_header(ahci, port, slot, &cmd);
|
||||
g_assert_cmphex(buffsize, ==, cmd.prdbc);
|
||||
}
|
||||
|
||||
/* Get the command in #slot of port #port. */
|
||||
void ahci_get_command_header(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t slot, AHCICommandHeader *cmd)
|
||||
{
|
||||
uint64_t ba = ahci->port[port].clb;
|
||||
ba += slot * sizeof(AHCICommandHeader);
|
||||
memread(ba, cmd, sizeof(AHCICommandHeader));
|
||||
|
||||
cmd->flags = le16_to_cpu(cmd->flags);
|
||||
cmd->prdtl = le16_to_cpu(cmd->prdtl);
|
||||
cmd->prdbc = le32_to_cpu(cmd->prdbc);
|
||||
cmd->ctba = le64_to_cpu(cmd->ctba);
|
||||
}
|
||||
|
||||
/* Set the command in #slot of port #port. */
|
||||
void ahci_set_command_header(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t slot, AHCICommandHeader *cmd)
|
||||
{
|
||||
AHCICommandHeader tmp;
|
||||
uint64_t ba = ahci->port[port].clb;
|
||||
ba += slot * sizeof(AHCICommandHeader);
|
||||
|
||||
tmp.flags = cpu_to_le16(cmd->flags);
|
||||
tmp.prdtl = cpu_to_le16(cmd->prdtl);
|
||||
tmp.prdbc = cpu_to_le32(cmd->prdbc);
|
||||
tmp.ctba = cpu_to_le64(cmd->ctba);
|
||||
|
||||
memwrite(ba, &tmp, sizeof(AHCICommandHeader));
|
||||
}
|
||||
|
||||
void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot)
|
||||
{
|
||||
AHCICommandHeader cmd;
|
||||
|
||||
/* Obtain the Nth Command Header */
|
||||
ahci_get_command_header(ahci, port, slot, &cmd);
|
||||
if (cmd.ctba == 0) {
|
||||
/* No address in it, so just return -- it's empty. */
|
||||
goto tidy;
|
||||
}
|
||||
|
||||
/* Free the Table */
|
||||
ahci_free(ahci, cmd.ctba);
|
||||
|
||||
tidy:
|
||||
/* NULL the header. */
|
||||
memset(&cmd, 0x00, sizeof(cmd));
|
||||
ahci_set_command_header(ahci, port, slot, &cmd);
|
||||
ahci->port[port].ctba[slot] = 0;
|
||||
ahci->port[port].prdtl[slot] = 0;
|
||||
}
|
||||
|
||||
void ahci_write_fis(AHCIQState *ahci, RegH2DFIS *fis, uint64_t addr)
|
||||
{
|
||||
RegH2DFIS tmp = *fis;
|
||||
|
||||
/* The auxiliary FIS fields are defined per-command and are not
|
||||
* currently implemented in libqos/ahci.o, but may or may not need
|
||||
* to be flipped. */
|
||||
|
||||
/* All other FIS fields are 8 bit and do not need to be flipped. */
|
||||
tmp.count = cpu_to_le16(tmp.count);
|
||||
|
||||
memwrite(addr, &tmp, sizeof(tmp));
|
||||
}
|
||||
|
||||
unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned j;
|
||||
uint32_t reg;
|
||||
|
||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_CI);
|
||||
|
||||
/* Pick the least recently used command slot that's available */
|
||||
for (i = 0; i < 32; ++i) {
|
||||
j = ((ahci->port[port].next + i) % 32);
|
||||
if (reg & (1 << j)) {
|
||||
continue;
|
||||
}
|
||||
ahci_destroy_command(ahci, port, i);
|
||||
ahci->port[port].next = (j + 1) % 32;
|
||||
return j;
|
||||
}
|
||||
|
||||
g_test_message("All command slots were busy.");
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
inline unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd)
|
||||
{
|
||||
/* Each PRD can describe up to 4MiB */
|
||||
g_assert_cmphex(bytes_per_prd, <=, 4096 * 1024);
|
||||
g_assert_cmphex(bytes_per_prd & 0x01, ==, 0x00);
|
||||
return (bytes + bytes_per_prd - 1) / bytes_per_prd;
|
||||
}
|
||||
|
||||
/* Given a guest buffer address, perform an IO operation */
|
||||
void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
|
||||
uint64_t buffer, size_t bufsize)
|
||||
{
|
||||
AHCICommand *cmd;
|
||||
|
||||
cmd = ahci_command_create(ide_cmd);
|
||||
ahci_command_set_buffer(cmd, buffer);
|
||||
ahci_command_set_size(cmd, bufsize);
|
||||
ahci_command_commit(ahci, cmd, port);
|
||||
ahci_command_issue(ahci, cmd);
|
||||
ahci_command_verify(ahci, cmd);
|
||||
ahci_command_free(cmd);
|
||||
}
|
||||
|
||||
struct AHCICommand {
|
||||
/* Test Management Data */
|
||||
uint8_t name;
|
||||
uint8_t port;
|
||||
uint8_t slot;
|
||||
uint32_t interrupts;
|
||||
uint64_t xbytes;
|
||||
uint32_t prd_size;
|
||||
uint64_t buffer;
|
||||
AHCICommandProp *props;
|
||||
/* Data to be transferred to the guest */
|
||||
AHCICommandHeader header;
|
||||
RegH2DFIS fis;
|
||||
void *atapi_cmd;
|
||||
};
|
||||
|
||||
static AHCICommandProp *ahci_command_find(uint8_t command_name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ahci_command_properties); i++) {
|
||||
if (ahci_command_properties[i].cmd == command_name) {
|
||||
return &ahci_command_properties[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Given a HOST buffer, create a buffer address and perform an IO operation. */
|
||||
void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
|
||||
void *buffer, size_t bufsize)
|
||||
{
|
||||
uint64_t ptr;
|
||||
AHCICommandProp *props;
|
||||
|
||||
props = ahci_command_find(ide_cmd);
|
||||
g_assert(props);
|
||||
ptr = ahci_alloc(ahci, bufsize);
|
||||
g_assert(ptr);
|
||||
|
||||
if (props->write) {
|
||||
memwrite(ptr, buffer, bufsize);
|
||||
}
|
||||
|
||||
ahci_guest_io(ahci, port, ide_cmd, ptr, bufsize);
|
||||
|
||||
if (props->read) {
|
||||
memread(ptr, buffer, bufsize);
|
||||
}
|
||||
|
||||
ahci_free(ahci, ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a basic command header in memory.
|
||||
* We assume that this is for an ATA command using RegH2DFIS.
|
||||
*/
|
||||
static void command_header_init(AHCICommand *cmd)
|
||||
{
|
||||
AHCICommandHeader *hdr = &cmd->header;
|
||||
AHCICommandProp *props = cmd->props;
|
||||
|
||||
hdr->flags = 5; /* RegH2DFIS is 5 DW long. Must be < 32 */
|
||||
hdr->flags |= CMDH_CLR_BSY; /* Clear the BSY bit when done */
|
||||
if (props->write) {
|
||||
hdr->flags |= CMDH_WRITE;
|
||||
}
|
||||
if (props->atapi) {
|
||||
hdr->flags |= CMDH_ATAPI;
|
||||
}
|
||||
/* Other flags: PREFETCH, RESET, and BIST */
|
||||
hdr->prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size);
|
||||
hdr->prdbc = 0;
|
||||
hdr->ctba = 0;
|
||||
}
|
||||
|
||||
static void command_table_init(AHCICommand *cmd)
|
||||
{
|
||||
RegH2DFIS *fis = &(cmd->fis);
|
||||
|
||||
fis->fis_type = REG_H2D_FIS;
|
||||
fis->flags = REG_H2D_FIS_CMD; /* "Command" bit */
|
||||
fis->command = cmd->name;
|
||||
cmd->fis.feature_low = 0x00;
|
||||
cmd->fis.feature_high = 0x00;
|
||||
if (cmd->props->lba28 || cmd->props->lba48) {
|
||||
cmd->fis.device = ATA_DEVICE_LBA;
|
||||
}
|
||||
cmd->fis.count = (cmd->xbytes / AHCI_SECTOR_SIZE);
|
||||
cmd->fis.icc = 0x00;
|
||||
cmd->fis.control = 0x00;
|
||||
memset(cmd->fis.aux, 0x00, ARRAY_SIZE(cmd->fis.aux));
|
||||
}
|
||||
|
||||
AHCICommand *ahci_command_create(uint8_t command_name)
|
||||
{
|
||||
AHCICommandProp *props = ahci_command_find(command_name);
|
||||
AHCICommand *cmd;
|
||||
|
||||
g_assert(props);
|
||||
cmd = g_malloc0(sizeof(AHCICommand));
|
||||
g_assert(!(props->dma && props->pio));
|
||||
g_assert(!(props->lba28 && props->lba48));
|
||||
g_assert(!(props->read && props->write));
|
||||
g_assert(!props->size || props->data);
|
||||
|
||||
/* Defaults and book-keeping */
|
||||
cmd->props = props;
|
||||
cmd->name = command_name;
|
||||
cmd->xbytes = props->size;
|
||||
cmd->prd_size = 4096;
|
||||
cmd->buffer = 0xabad1dea;
|
||||
|
||||
cmd->interrupts = AHCI_PX_IS_DHRS;
|
||||
/* BUG: We expect the DPS interrupt for data commands */
|
||||
/* cmd->interrupts |= props->data ? AHCI_PX_IS_DPS : 0; */
|
||||
/* BUG: We expect the DMA Setup interrupt for DMA commands */
|
||||
/* cmd->interrupts |= props->dma ? AHCI_PX_IS_DSS : 0; */
|
||||
cmd->interrupts |= props->pio ? AHCI_PX_IS_PSS : 0;
|
||||
|
||||
command_header_init(cmd);
|
||||
command_table_init(cmd);
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void ahci_command_free(AHCICommand *cmd)
|
||||
{
|
||||
g_free(cmd);
|
||||
}
|
||||
|
||||
void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer)
|
||||
{
|
||||
cmd->buffer = buffer;
|
||||
}
|
||||
|
||||
void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
|
||||
unsigned prd_size)
|
||||
{
|
||||
/* Each PRD can describe up to 4MiB, and must not be odd. */
|
||||
g_assert_cmphex(prd_size, <=, 4096 * 1024);
|
||||
g_assert_cmphex(prd_size & 0x01, ==, 0x00);
|
||||
cmd->prd_size = prd_size;
|
||||
cmd->xbytes = xbytes;
|
||||
cmd->fis.count = (cmd->xbytes / AHCI_SECTOR_SIZE);
|
||||
cmd->header.prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size);
|
||||
}
|
||||
|
||||
void ahci_command_set_size(AHCICommand *cmd, uint64_t xbytes)
|
||||
{
|
||||
ahci_command_set_sizes(cmd, xbytes, cmd->prd_size);
|
||||
}
|
||||
|
||||
void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size)
|
||||
{
|
||||
ahci_command_set_sizes(cmd, cmd->xbytes, prd_size);
|
||||
}
|
||||
|
||||
void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port)
|
||||
{
|
||||
uint16_t i, prdtl;
|
||||
uint64_t table_size, table_ptr, remaining;
|
||||
PRD prd;
|
||||
|
||||
/* This command is now tied to this port/command slot */
|
||||
cmd->port = port;
|
||||
cmd->slot = ahci_pick_cmd(ahci, port);
|
||||
|
||||
/* Create a buffer for the command table */
|
||||
prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size);
|
||||
table_size = CMD_TBL_SIZ(prdtl);
|
||||
table_ptr = ahci_alloc(ahci, table_size);
|
||||
g_assert(table_ptr);
|
||||
/* AHCI 1.3: Must be aligned to 0x80 */
|
||||
g_assert((table_ptr & 0x7F) == 0x00);
|
||||
cmd->header.ctba = table_ptr;
|
||||
|
||||
/* Commit the command header and command FIS */
|
||||
ahci_set_command_header(ahci, port, cmd->slot, &(cmd->header));
|
||||
ahci_write_fis(ahci, &(cmd->fis), table_ptr);
|
||||
|
||||
/* Construct and write the PRDs to the command table */
|
||||
g_assert_cmphex(prdtl, ==, cmd->header.prdtl);
|
||||
remaining = cmd->xbytes;
|
||||
for (i = 0; i < prdtl; ++i) {
|
||||
prd.dba = cpu_to_le64(cmd->buffer + (cmd->prd_size * i));
|
||||
prd.res = 0;
|
||||
if (remaining > cmd->prd_size) {
|
||||
/* Note that byte count is 0-based. */
|
||||
prd.dbc = cpu_to_le32(cmd->prd_size - 1);
|
||||
remaining -= cmd->prd_size;
|
||||
} else {
|
||||
/* Again, dbc is 0-based. */
|
||||
prd.dbc = cpu_to_le32(remaining - 1);
|
||||
remaining = 0;
|
||||
}
|
||||
prd.dbc |= cpu_to_le32(0x80000000); /* Request DPS Interrupt */
|
||||
|
||||
/* Commit the PRD entry to the Command Table */
|
||||
memwrite(table_ptr + 0x80 + (i * sizeof(PRD)),
|
||||
&prd, sizeof(PRD));
|
||||
}
|
||||
|
||||
/* Bookmark the PRDTL and CTBA values */
|
||||
ahci->port[port].ctba[cmd->slot] = table_ptr;
|
||||
ahci->port[port].prdtl[cmd->slot] = prdtl;
|
||||
}
|
||||
|
||||
void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd)
|
||||
{
|
||||
if (cmd->props->ncq) {
|
||||
ahci_px_wreg(ahci, cmd->port, AHCI_PX_SACT, (1 << cmd->slot));
|
||||
}
|
||||
|
||||
ahci_px_wreg(ahci, cmd->port, AHCI_PX_CI, (1 << cmd->slot));
|
||||
}
|
||||
|
||||
void ahci_command_wait(AHCIQState *ahci, AHCICommand *cmd)
|
||||
{
|
||||
/* We can't rely on STS_BSY until the command has started processing.
|
||||
* Therefore, we also use the Command Issue bit as indication of
|
||||
* a command in-flight. */
|
||||
while (BITSET(ahci_px_rreg(ahci, cmd->port, AHCI_PX_TFD),
|
||||
AHCI_PX_TFD_STS_BSY) ||
|
||||
BITSET(ahci_px_rreg(ahci, cmd->port, AHCI_PX_CI), (1 << cmd->slot))) {
|
||||
usleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd)
|
||||
{
|
||||
ahci_command_issue_async(ahci, cmd);
|
||||
ahci_command_wait(ahci, cmd);
|
||||
}
|
||||
|
||||
void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd)
|
||||
{
|
||||
uint8_t slot = cmd->slot;
|
||||
uint8_t port = cmd->port;
|
||||
|
||||
ahci_port_check_error(ahci, port);
|
||||
ahci_port_check_interrupts(ahci, port, cmd->interrupts);
|
||||
ahci_port_check_nonbusy(ahci, port, slot);
|
||||
ahci_port_check_cmd_sanity(ahci, port, slot, cmd->xbytes);
|
||||
ahci_port_check_d2h_sanity(ahci, port, slot);
|
||||
if (cmd->props->pio) {
|
||||
ahci_port_check_pio_sanity(ahci, port, slot, cmd->xbytes);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ahci_command_slot(AHCICommand *cmd)
|
||||
{
|
||||
return cmd->slot;
|
||||
}
|
|
@ -0,0 +1,549 @@
|
|||
#ifndef __libqos_ahci_h
|
||||
#define __libqos_ahci_h
|
||||
|
||||
/*
|
||||
* AHCI qtest library functions and definitions
|
||||
*
|
||||
* Copyright (c) 2014 John Snow <jsnow@redhat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "libqos/libqos.h"
|
||||
#include "libqos/pci.h"
|
||||
#include "libqos/malloc-pc.h"
|
||||
|
||||
/*** Supplementary PCI Config Space IDs & Masks ***/
|
||||
#define PCI_DEVICE_ID_INTEL_Q35_AHCI (0x2922)
|
||||
#define PCI_MSI_FLAGS_RESERVED (0xFF00)
|
||||
#define PCI_PM_CTRL_RESERVED (0xFC)
|
||||
#define PCI_BCC(REG32) ((REG32) >> 24)
|
||||
#define PCI_PI(REG32) (((REG32) >> 8) & 0xFF)
|
||||
#define PCI_SCC(REG32) (((REG32) >> 16) & 0xFF)
|
||||
|
||||
/*** Recognized AHCI Device Types ***/
|
||||
#define AHCI_INTEL_ICH9 (PCI_DEVICE_ID_INTEL_Q35_AHCI << 16 | \
|
||||
PCI_VENDOR_ID_INTEL)
|
||||
|
||||
/*** AHCI/HBA Register Offsets and Bitmasks ***/
|
||||
#define AHCI_CAP (0)
|
||||
#define AHCI_CAP_NP (0x1F)
|
||||
#define AHCI_CAP_SXS (0x20)
|
||||
#define AHCI_CAP_EMS (0x40)
|
||||
#define AHCI_CAP_CCCS (0x80)
|
||||
#define AHCI_CAP_NCS (0x1F00)
|
||||
#define AHCI_CAP_PSC (0x2000)
|
||||
#define AHCI_CAP_SSC (0x4000)
|
||||
#define AHCI_CAP_PMD (0x8000)
|
||||
#define AHCI_CAP_FBSS (0x10000)
|
||||
#define AHCI_CAP_SPM (0x20000)
|
||||
#define AHCI_CAP_SAM (0x40000)
|
||||
#define AHCI_CAP_RESERVED (0x80000)
|
||||
#define AHCI_CAP_ISS (0xF00000)
|
||||
#define AHCI_CAP_SCLO (0x1000000)
|
||||
#define AHCI_CAP_SAL (0x2000000)
|
||||
#define AHCI_CAP_SALP (0x4000000)
|
||||
#define AHCI_CAP_SSS (0x8000000)
|
||||
#define AHCI_CAP_SMPS (0x10000000)
|
||||
#define AHCI_CAP_SSNTF (0x20000000)
|
||||
#define AHCI_CAP_SNCQ (0x40000000)
|
||||
#define AHCI_CAP_S64A (0x80000000)
|
||||
|
||||
#define AHCI_GHC (1)
|
||||
#define AHCI_GHC_HR (0x01)
|
||||
#define AHCI_GHC_IE (0x02)
|
||||
#define AHCI_GHC_MRSM (0x04)
|
||||
#define AHCI_GHC_RESERVED (0x7FFFFFF8)
|
||||
#define AHCI_GHC_AE (0x80000000)
|
||||
|
||||
#define AHCI_IS (2)
|
||||
#define AHCI_PI (3)
|
||||
#define AHCI_VS (4)
|
||||
|
||||
#define AHCI_CCCCTL (5)
|
||||
#define AHCI_CCCCTL_EN (0x01)
|
||||
#define AHCI_CCCCTL_RESERVED (0x06)
|
||||
#define AHCI_CCCCTL_CC (0xFF00)
|
||||
#define AHCI_CCCCTL_TV (0xFFFF0000)
|
||||
|
||||
#define AHCI_CCCPORTS (6)
|
||||
#define AHCI_EMLOC (7)
|
||||
|
||||
#define AHCI_EMCTL (8)
|
||||
#define AHCI_EMCTL_STSMR (0x01)
|
||||
#define AHCI_EMCTL_CTLTM (0x100)
|
||||
#define AHCI_EMCTL_CTLRST (0x200)
|
||||
#define AHCI_EMCTL_RESERVED (0xF0F0FCFE)
|
||||
|
||||
#define AHCI_CAP2 (9)
|
||||
#define AHCI_CAP2_BOH (0x01)
|
||||
#define AHCI_CAP2_NVMP (0x02)
|
||||
#define AHCI_CAP2_APST (0x04)
|
||||
#define AHCI_CAP2_RESERVED (0xFFFFFFF8)
|
||||
|
||||
#define AHCI_BOHC (10)
|
||||
#define AHCI_RESERVED (11)
|
||||
#define AHCI_NVMHCI (24)
|
||||
#define AHCI_VENDOR (40)
|
||||
#define AHCI_PORTS (64)
|
||||
|
||||
/*** Port Memory Offsets & Bitmasks ***/
|
||||
#define AHCI_PX_CLB (0)
|
||||
#define AHCI_PX_CLB_RESERVED (0x1FF)
|
||||
|
||||
#define AHCI_PX_CLBU (1)
|
||||
|
||||
#define AHCI_PX_FB (2)
|
||||
#define AHCI_PX_FB_RESERVED (0xFF)
|
||||
|
||||
#define AHCI_PX_FBU (3)
|
||||
|
||||
#define AHCI_PX_IS (4)
|
||||
#define AHCI_PX_IS_DHRS (0x1)
|
||||
#define AHCI_PX_IS_PSS (0x2)
|
||||
#define AHCI_PX_IS_DSS (0x4)
|
||||
#define AHCI_PX_IS_SDBS (0x8)
|
||||
#define AHCI_PX_IS_UFS (0x10)
|
||||
#define AHCI_PX_IS_DPS (0x20)
|
||||
#define AHCI_PX_IS_PCS (0x40)
|
||||
#define AHCI_PX_IS_DMPS (0x80)
|
||||
#define AHCI_PX_IS_RESERVED (0x23FFF00)
|
||||
#define AHCI_PX_IS_PRCS (0x400000)
|
||||
#define AHCI_PX_IS_IPMS (0x800000)
|
||||
#define AHCI_PX_IS_OFS (0x1000000)
|
||||
#define AHCI_PX_IS_INFS (0x4000000)
|
||||
#define AHCI_PX_IS_IFS (0x8000000)
|
||||
#define AHCI_PX_IS_HBDS (0x10000000)
|
||||
#define AHCI_PX_IS_HBFS (0x20000000)
|
||||
#define AHCI_PX_IS_TFES (0x40000000)
|
||||
#define AHCI_PX_IS_CPDS (0x80000000)
|
||||
|
||||
#define AHCI_PX_IE (5)
|
||||
#define AHCI_PX_IE_DHRE (0x1)
|
||||
#define AHCI_PX_IE_PSE (0x2)
|
||||
#define AHCI_PX_IE_DSE (0x4)
|
||||
#define AHCI_PX_IE_SDBE (0x8)
|
||||
#define AHCI_PX_IE_UFE (0x10)
|
||||
#define AHCI_PX_IE_DPE (0x20)
|
||||
#define AHCI_PX_IE_PCE (0x40)
|
||||
#define AHCI_PX_IE_DMPE (0x80)
|
||||
#define AHCI_PX_IE_RESERVED (0x23FFF00)
|
||||
#define AHCI_PX_IE_PRCE (0x400000)
|
||||
#define AHCI_PX_IE_IPME (0x800000)
|
||||
#define AHCI_PX_IE_OFE (0x1000000)
|
||||
#define AHCI_PX_IE_INFE (0x4000000)
|
||||
#define AHCI_PX_IE_IFE (0x8000000)
|
||||
#define AHCI_PX_IE_HBDE (0x10000000)
|
||||
#define AHCI_PX_IE_HBFE (0x20000000)
|
||||
#define AHCI_PX_IE_TFEE (0x40000000)
|
||||
#define AHCI_PX_IE_CPDE (0x80000000)
|
||||
|
||||
#define AHCI_PX_CMD (6)
|
||||
#define AHCI_PX_CMD_ST (0x1)
|
||||
#define AHCI_PX_CMD_SUD (0x2)
|
||||
#define AHCI_PX_CMD_POD (0x4)
|
||||
#define AHCI_PX_CMD_CLO (0x8)
|
||||
#define AHCI_PX_CMD_FRE (0x10)
|
||||
#define AHCI_PX_CMD_RESERVED (0xE0)
|
||||
#define AHCI_PX_CMD_CCS (0x1F00)
|
||||
#define AHCI_PX_CMD_MPSS (0x2000)
|
||||
#define AHCI_PX_CMD_FR (0x4000)
|
||||
#define AHCI_PX_CMD_CR (0x8000)
|
||||
#define AHCI_PX_CMD_CPS (0x10000)
|
||||
#define AHCI_PX_CMD_PMA (0x20000)
|
||||
#define AHCI_PX_CMD_HPCP (0x40000)
|
||||
#define AHCI_PX_CMD_MPSP (0x80000)
|
||||
#define AHCI_PX_CMD_CPD (0x100000)
|
||||
#define AHCI_PX_CMD_ESP (0x200000)
|
||||
#define AHCI_PX_CMD_FBSCP (0x400000)
|
||||
#define AHCI_PX_CMD_APSTE (0x800000)
|
||||
#define AHCI_PX_CMD_ATAPI (0x1000000)
|
||||
#define AHCI_PX_CMD_DLAE (0x2000000)
|
||||
#define AHCI_PX_CMD_ALPE (0x4000000)
|
||||
#define AHCI_PX_CMD_ASP (0x8000000)
|
||||
#define AHCI_PX_CMD_ICC (0xF0000000)
|
||||
|
||||
#define AHCI_PX_RES1 (7)
|
||||
|
||||
#define AHCI_PX_TFD (8)
|
||||
#define AHCI_PX_TFD_STS (0xFF)
|
||||
#define AHCI_PX_TFD_STS_ERR (0x01)
|
||||
#define AHCI_PX_TFD_STS_CS1 (0x06)
|
||||
#define AHCI_PX_TFD_STS_DRQ (0x08)
|
||||
#define AHCI_PX_TFD_STS_CS2 (0x70)
|
||||
#define AHCI_PX_TFD_STS_BSY (0x80)
|
||||
#define AHCI_PX_TFD_ERR (0xFF00)
|
||||
#define AHCI_PX_TFD_RESERVED (0xFFFF0000)
|
||||
|
||||
#define AHCI_PX_SIG (9)
|
||||
#define AHCI_PX_SIG_SECTOR_COUNT (0xFF)
|
||||
#define AHCI_PX_SIG_LBA_LOW (0xFF00)
|
||||
#define AHCI_PX_SIG_LBA_MID (0xFF0000)
|
||||
#define AHCI_PX_SIG_LBA_HIGH (0xFF000000)
|
||||
|
||||
#define AHCI_PX_SSTS (10)
|
||||
#define AHCI_PX_SSTS_DET (0x0F)
|
||||
#define AHCI_PX_SSTS_SPD (0xF0)
|
||||
#define AHCI_PX_SSTS_IPM (0xF00)
|
||||
#define AHCI_PX_SSTS_RESERVED (0xFFFFF000)
|
||||
#define SSTS_DET_NO_DEVICE (0x00)
|
||||
#define SSTS_DET_PRESENT (0x01)
|
||||
#define SSTS_DET_ESTABLISHED (0x03)
|
||||
#define SSTS_DET_OFFLINE (0x04)
|
||||
|
||||
#define AHCI_PX_SCTL (11)
|
||||
|
||||
#define AHCI_PX_SERR (12)
|
||||
#define AHCI_PX_SERR_ERR (0xFFFF)
|
||||
#define AHCI_PX_SERR_DIAG (0xFFFF0000)
|
||||
#define AHCI_PX_SERR_DIAG_X (0x04000000)
|
||||
|
||||
#define AHCI_PX_SACT (13)
|
||||
#define AHCI_PX_CI (14)
|
||||
#define AHCI_PX_SNTF (15)
|
||||
|
||||
#define AHCI_PX_FBS (16)
|
||||
#define AHCI_PX_FBS_EN (0x1)
|
||||
#define AHCI_PX_FBS_DEC (0x2)
|
||||
#define AHCI_PX_FBS_SDE (0x4)
|
||||
#define AHCI_PX_FBS_DEV (0xF00)
|
||||
#define AHCI_PX_FBS_ADO (0xF000)
|
||||
#define AHCI_PX_FBS_DWE (0xF0000)
|
||||
#define AHCI_PX_FBS_RESERVED (0xFFF000F8)
|
||||
|
||||
#define AHCI_PX_RES2 (17)
|
||||
#define AHCI_PX_VS (28)
|
||||
|
||||
#define HBA_DATA_REGION_SIZE (256)
|
||||
#define HBA_PORT_DATA_SIZE (128)
|
||||
#define HBA_PORT_NUM_REG (HBA_PORT_DATA_SIZE/4)
|
||||
|
||||
#define AHCI_VERSION_0_95 (0x00000905)
|
||||
#define AHCI_VERSION_1_0 (0x00010000)
|
||||
#define AHCI_VERSION_1_1 (0x00010100)
|
||||
#define AHCI_VERSION_1_2 (0x00010200)
|
||||
#define AHCI_VERSION_1_3 (0x00010300)
|
||||
|
||||
#define AHCI_SECTOR_SIZE (512)
|
||||
|
||||
/* FIS types */
|
||||
enum {
|
||||
REG_H2D_FIS = 0x27,
|
||||
REG_D2H_FIS = 0x34,
|
||||
DMA_ACTIVATE_FIS = 0x39,
|
||||
DMA_SETUP_FIS = 0x41,
|
||||
DATA_FIS = 0x46,
|
||||
BIST_ACTIVATE_FIS = 0x58,
|
||||
PIO_SETUP_FIS = 0x5F,
|
||||
SDB_FIS = 0xA1
|
||||
};
|
||||
|
||||
/* FIS flags */
|
||||
#define REG_H2D_FIS_CMD 0x80
|
||||
|
||||
/* ATA Commands */
|
||||
enum {
|
||||
/* DMA */
|
||||
CMD_READ_DMA = 0xC8,
|
||||
CMD_READ_DMA_EXT = 0x25,
|
||||
CMD_WRITE_DMA = 0xCA,
|
||||
CMD_WRITE_DMA_EXT = 0x35,
|
||||
/* PIO */
|
||||
CMD_READ_PIO = 0x20,
|
||||
CMD_READ_PIO_EXT = 0x24,
|
||||
CMD_WRITE_PIO = 0x30,
|
||||
CMD_WRITE_PIO_EXT = 0x34,
|
||||
/* Misc */
|
||||
CMD_READ_MAX = 0xF8,
|
||||
CMD_READ_MAX_EXT = 0x27,
|
||||
CMD_FLUSH_CACHE = 0xE7,
|
||||
CMD_IDENTIFY = 0xEC
|
||||
};
|
||||
|
||||
/* AHCI Command Header Flags & Masks*/
|
||||
#define CMDH_CFL (0x1F)
|
||||
#define CMDH_ATAPI (0x20)
|
||||
#define CMDH_WRITE (0x40)
|
||||
#define CMDH_PREFETCH (0x80)
|
||||
#define CMDH_RESET (0x100)
|
||||
#define CMDH_BIST (0x200)
|
||||
#define CMDH_CLR_BSY (0x400)
|
||||
#define CMDH_RES (0x800)
|
||||
#define CMDH_PMP (0xF000)
|
||||
|
||||
/* ATA device register masks */
|
||||
#define ATA_DEVICE_MAGIC 0xA0
|
||||
#define ATA_DEVICE_LBA 0x40
|
||||
#define ATA_DEVICE_DRIVE 0x10
|
||||
#define ATA_DEVICE_HEAD 0x0F
|
||||
|
||||
/*** Structures ***/
|
||||
|
||||
typedef struct AHCIPortQState {
|
||||
uint64_t fb;
|
||||
uint64_t clb;
|
||||
uint64_t ctba[32];
|
||||
uint16_t prdtl[32];
|
||||
uint8_t next; /** Next Command Slot to Use **/
|
||||
} AHCIPortQState;
|
||||
|
||||
typedef struct AHCIQState {
|
||||
QOSState *parent;
|
||||
QPCIDevice *dev;
|
||||
void *hba_base;
|
||||
uint64_t barsize;
|
||||
uint32_t fingerprint;
|
||||
uint32_t cap;
|
||||
uint32_t cap2;
|
||||
AHCIPortQState port[32];
|
||||
} AHCIQState;
|
||||
|
||||
/**
|
||||
* Generic FIS structure.
|
||||
*/
|
||||
typedef struct FIS {
|
||||
uint8_t fis_type;
|
||||
uint8_t flags;
|
||||
char data[0];
|
||||
} __attribute__((__packed__)) FIS;
|
||||
|
||||
/**
|
||||
* Register device-to-host FIS structure.
|
||||
*/
|
||||
typedef struct RegD2HFIS {
|
||||
/* DW0 */
|
||||
uint8_t fis_type;
|
||||
uint8_t flags;
|
||||
uint8_t status;
|
||||
uint8_t error;
|
||||
/* DW1 */
|
||||
uint8_t lba_lo[3];
|
||||
uint8_t device;
|
||||
/* DW2 */
|
||||
uint8_t lba_hi[3];
|
||||
uint8_t res0;
|
||||
/* DW3 */
|
||||
uint16_t count;
|
||||
uint16_t res1;
|
||||
/* DW4 */
|
||||
uint32_t res2;
|
||||
} __attribute__((__packed__)) RegD2HFIS;
|
||||
|
||||
/**
|
||||
* Register device-to-host FIS structure;
|
||||
* PIO Setup variety.
|
||||
*/
|
||||
typedef struct PIOSetupFIS {
|
||||
/* DW0 */
|
||||
uint8_t fis_type;
|
||||
uint8_t flags;
|
||||
uint8_t status;
|
||||
uint8_t error;
|
||||
/* DW1 */
|
||||
uint8_t lba_lo[3];
|
||||
uint8_t device;
|
||||
/* DW2 */
|
||||
uint8_t lba_hi[3];
|
||||
uint8_t res0;
|
||||
/* DW3 */
|
||||
uint16_t count;
|
||||
uint8_t res1;
|
||||
uint8_t e_status;
|
||||
/* DW4 */
|
||||
uint16_t tx_count;
|
||||
uint16_t res2;
|
||||
} __attribute__((__packed__)) PIOSetupFIS;
|
||||
|
||||
/**
|
||||
* Register host-to-device FIS structure.
|
||||
*/
|
||||
typedef struct RegH2DFIS {
|
||||
/* DW0 */
|
||||
uint8_t fis_type;
|
||||
uint8_t flags;
|
||||
uint8_t command;
|
||||
uint8_t feature_low;
|
||||
/* DW1 */
|
||||
uint8_t lba_lo[3];
|
||||
uint8_t device;
|
||||
/* DW2 */
|
||||
uint8_t lba_hi[3];
|
||||
uint8_t feature_high;
|
||||
/* DW3 */
|
||||
uint16_t count;
|
||||
uint8_t icc;
|
||||
uint8_t control;
|
||||
/* DW4 */
|
||||
uint8_t aux[4];
|
||||
} __attribute__((__packed__)) RegH2DFIS;
|
||||
|
||||
/**
|
||||
* Command List entry structure.
|
||||
* The command list contains between 1-32 of these structures.
|
||||
*/
|
||||
typedef struct AHCICommandHeader {
|
||||
uint16_t flags; /* Cmd-Fis-Len, PMP#, and flags. */
|
||||
uint16_t prdtl; /* Phys Region Desc. Table Length */
|
||||
uint32_t prdbc; /* Phys Region Desc. Byte Count */
|
||||
uint64_t ctba; /* Command Table Descriptor Base Address */
|
||||
uint32_t res[4];
|
||||
} __attribute__((__packed__)) AHCICommandHeader;
|
||||
|
||||
/**
|
||||
* Physical Region Descriptor; pointed to by the Command List Header,
|
||||
* struct ahci_command.
|
||||
*/
|
||||
typedef struct PRD {
|
||||
uint64_t dba; /* Data Base Address */
|
||||
uint32_t res; /* Reserved */
|
||||
uint32_t dbc; /* Data Byte Count (0-indexed) & Interrupt Flag (bit 2^31) */
|
||||
} __attribute__((__packed__)) PRD;
|
||||
|
||||
/* Opaque, defined within ahci.c */
|
||||
typedef struct AHCICommand AHCICommand;
|
||||
|
||||
/*** Macro Utilities ***/
|
||||
#define BITANY(data, mask) (((data) & (mask)) != 0)
|
||||
#define BITSET(data, mask) (((data) & (mask)) == (mask))
|
||||
#define BITCLR(data, mask) (((data) & (mask)) == 0)
|
||||
#define ASSERT_BIT_SET(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
|
||||
#define ASSERT_BIT_CLEAR(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
|
||||
|
||||
/* For calculating how big the PRD table needs to be: */
|
||||
#define CMD_TBL_SIZ(n) ((0x80 + ((n) * sizeof(PRD)) + 0x7F) & ~0x7F)
|
||||
|
||||
/* Helpers for reading/writing AHCI HBA register values */
|
||||
|
||||
static inline uint32_t ahci_mread(AHCIQState *ahci, size_t offset)
|
||||
{
|
||||
return qpci_io_readl(ahci->dev, ahci->hba_base + offset);
|
||||
}
|
||||
|
||||
static inline void ahci_mwrite(AHCIQState *ahci, size_t offset, uint32_t value)
|
||||
{
|
||||
qpci_io_writel(ahci->dev, ahci->hba_base + offset, value);
|
||||
}
|
||||
|
||||
static inline uint32_t ahci_rreg(AHCIQState *ahci, uint32_t reg_num)
|
||||
{
|
||||
return ahci_mread(ahci, 4 * reg_num);
|
||||
}
|
||||
|
||||
static inline void ahci_wreg(AHCIQState *ahci, uint32_t reg_num, uint32_t value)
|
||||
{
|
||||
ahci_mwrite(ahci, 4 * reg_num, value);
|
||||
}
|
||||
|
||||
static inline void ahci_set(AHCIQState *ahci, uint32_t reg_num, uint32_t mask)
|
||||
{
|
||||
ahci_wreg(ahci, reg_num, ahci_rreg(ahci, reg_num) | mask);
|
||||
}
|
||||
|
||||
static inline void ahci_clr(AHCIQState *ahci, uint32_t reg_num, uint32_t mask)
|
||||
{
|
||||
ahci_wreg(ahci, reg_num, ahci_rreg(ahci, reg_num) & ~mask);
|
||||
}
|
||||
|
||||
static inline size_t ahci_px_offset(uint8_t port, uint32_t reg_num)
|
||||
{
|
||||
return AHCI_PORTS + (HBA_PORT_NUM_REG * port) + reg_num;
|
||||
}
|
||||
|
||||
static inline uint32_t ahci_px_rreg(AHCIQState *ahci, uint8_t port,
|
||||
uint32_t reg_num)
|
||||
{
|
||||
return ahci_rreg(ahci, ahci_px_offset(port, reg_num));
|
||||
}
|
||||
|
||||
static inline void ahci_px_wreg(AHCIQState *ahci, uint8_t port,
|
||||
uint32_t reg_num, uint32_t value)
|
||||
{
|
||||
ahci_wreg(ahci, ahci_px_offset(port, reg_num), value);
|
||||
}
|
||||
|
||||
static inline void ahci_px_set(AHCIQState *ahci, uint8_t port,
|
||||
uint32_t reg_num, uint32_t mask)
|
||||
{
|
||||
ahci_px_wreg(ahci, port, reg_num,
|
||||
ahci_px_rreg(ahci, port, reg_num) | mask);
|
||||
}
|
||||
|
||||
static inline void ahci_px_clr(AHCIQState *ahci, uint8_t port,
|
||||
uint32_t reg_num, uint32_t mask)
|
||||
{
|
||||
ahci_px_wreg(ahci, port, reg_num,
|
||||
ahci_px_rreg(ahci, port, reg_num) & ~mask);
|
||||
}
|
||||
|
||||
/*** Prototypes ***/
|
||||
uint64_t ahci_alloc(AHCIQState *ahci, size_t bytes);
|
||||
void ahci_free(AHCIQState *ahci, uint64_t addr);
|
||||
QPCIDevice *get_ahci_device(uint32_t *fingerprint);
|
||||
void free_ahci_device(QPCIDevice *dev);
|
||||
void ahci_clean_mem(AHCIQState *ahci);
|
||||
void ahci_pci_enable(AHCIQState *ahci);
|
||||
void start_ahci_device(AHCIQState *ahci);
|
||||
void ahci_hba_enable(AHCIQState *ahci);
|
||||
unsigned ahci_port_select(AHCIQState *ahci);
|
||||
void ahci_port_clear(AHCIQState *ahci, uint8_t port);
|
||||
void ahci_port_check_error(AHCIQState *ahci, uint8_t port);
|
||||
void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
|
||||
uint32_t intr_mask);
|
||||
void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot);
|
||||
void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot);
|
||||
void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t slot, size_t buffsize);
|
||||
void ahci_port_check_cmd_sanity(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t slot, size_t buffsize);
|
||||
void ahci_get_command_header(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t slot, AHCICommandHeader *cmd);
|
||||
void ahci_set_command_header(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t slot, AHCICommandHeader *cmd);
|
||||
void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot);
|
||||
void ahci_write_fis(AHCIQState *ahci, RegH2DFIS *fis, uint64_t addr);
|
||||
unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port);
|
||||
unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd);
|
||||
void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
|
||||
uint64_t gbuffer, size_t size);
|
||||
void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
|
||||
void *buffer, size_t bufsize);
|
||||
|
||||
/* Command Lifecycle */
|
||||
AHCICommand *ahci_command_create(uint8_t command_name);
|
||||
void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port);
|
||||
void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd);
|
||||
void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd);
|
||||
void ahci_command_wait(AHCIQState *ahci, AHCICommand *cmd);
|
||||
void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd);
|
||||
void ahci_command_free(AHCICommand *cmd);
|
||||
|
||||
/* Command adjustments */
|
||||
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);
|
||||
|
||||
/* Command Misc */
|
||||
uint8_t ahci_command_slot(AHCICommand *cmd);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,24 @@
|
|||
#include "libqos/libqos-pc.h"
|
||||
#include "libqos/malloc-pc.h"
|
||||
|
||||
static QOSOps qos_ops = {
|
||||
.init_allocator = pc_alloc_init_flags,
|
||||
.uninit_allocator = pc_alloc_uninit
|
||||
};
|
||||
|
||||
QOSState *qtest_pc_boot(const char *cmdline_fmt, ...)
|
||||
{
|
||||
QOSState *qs;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, cmdline_fmt);
|
||||
qs = qtest_vboot(&qos_ops, cmdline_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return qs;
|
||||
}
|
||||
|
||||
void qtest_pc_shutdown(QOSState *qs)
|
||||
{
|
||||
return qtest_shutdown(qs);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef __libqos_pc_h
|
||||
#define __libqos_pc_h
|
||||
|
||||
#include "libqos/libqos.h"
|
||||
|
||||
QOSState *qtest_pc_boot(const char *cmdline_fmt, ...);
|
||||
void qtest_pc_shutdown(QOSState *qs);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,63 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <glib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "libqtest.h"
|
||||
#include "libqos/libqos.h"
|
||||
#include "libqos/pci.h"
|
||||
|
||||
/*** Test Setup & Teardown ***/
|
||||
|
||||
/**
|
||||
* Launch QEMU with the given command line,
|
||||
* and then set up interrupts and our guest malloc interface.
|
||||
*/
|
||||
QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap)
|
||||
{
|
||||
char *cmdline;
|
||||
|
||||
struct QOSState *qs = g_malloc(sizeof(QOSState));
|
||||
|
||||
cmdline = g_strdup_vprintf(cmdline_fmt, ap);
|
||||
qs->qts = qtest_start(cmdline);
|
||||
qs->ops = ops;
|
||||
qtest_irq_intercept_in(global_qtest, "ioapic");
|
||||
if (ops && ops->init_allocator) {
|
||||
qs->alloc = ops->init_allocator(ALLOC_NO_FLAGS);
|
||||
}
|
||||
|
||||
g_free(cmdline);
|
||||
return qs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch QEMU with the given command line,
|
||||
* and then set up interrupts and our guest malloc interface.
|
||||
*/
|
||||
QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...)
|
||||
{
|
||||
QOSState *qs;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, cmdline_fmt);
|
||||
qs = qtest_vboot(ops, cmdline_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return qs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tear down the QEMU instance.
|
||||
*/
|
||||
void qtest_shutdown(QOSState *qs)
|
||||
{
|
||||
if (qs->alloc && qs->ops && qs->ops->uninit_allocator) {
|
||||
qs->ops->uninit_allocator(qs->alloc);
|
||||
qs->alloc = NULL;
|
||||
}
|
||||
qtest_quit(qs->qts);
|
||||
g_free(qs);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef __libqos_h
|
||||
#define __libqos_h
|
||||
|
||||
#include "libqtest.h"
|
||||
#include "libqos/pci.h"
|
||||
#include "libqos/malloc-pc.h"
|
||||
|
||||
typedef struct QOSOps {
|
||||
QGuestAllocator *(*init_allocator)(QAllocOpts);
|
||||
void (*uninit_allocator)(QGuestAllocator *);
|
||||
} QOSOps;
|
||||
|
||||
typedef struct QOSState {
|
||||
QTestState *qts;
|
||||
QGuestAllocator *alloc;
|
||||
QOSOps *ops;
|
||||
} QOSState;
|
||||
|
||||
QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap);
|
||||
QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...);
|
||||
void qtest_shutdown(QOSState *qs);
|
||||
|
||||
static inline uint64_t qmalloc(QOSState *q, size_t bytes)
|
||||
{
|
||||
return guest_alloc(q->alloc, bytes);
|
||||
}
|
||||
|
||||
static inline void qfree(QOSState *q, uint64_t addr)
|
||||
{
|
||||
guest_free(q->alloc, addr);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -32,31 +32,17 @@ void pc_alloc_uninit(QGuestAllocator *allocator)
|
|||
|
||||
QGuestAllocator *pc_alloc_init_flags(QAllocOpts flags)
|
||||
{
|
||||
QGuestAllocator *s = g_malloc0(sizeof(*s));
|
||||
QGuestAllocator *s;
|
||||
uint64_t ram_size;
|
||||
QFWCFG *fw_cfg = pc_fw_cfg_init();
|
||||
MemBlock *node;
|
||||
|
||||
s->opts = flags;
|
||||
s->page_size = PAGE_SIZE;
|
||||
|
||||
ram_size = qfw_cfg_get_u64(fw_cfg, FW_CFG_RAM_SIZE);
|
||||
|
||||
/* Start at 1MB */
|
||||
s->start = 1 << 20;
|
||||
|
||||
/* Respect PCI hole */
|
||||
s->end = MIN(ram_size, 0xE0000000);
|
||||
s = alloc_init_flags(flags, 1 << 20, MIN(ram_size, 0xE0000000));
|
||||
alloc_set_page_size(s, PAGE_SIZE);
|
||||
|
||||
/* clean-up */
|
||||
g_free(fw_cfg);
|
||||
|
||||
QTAILQ_INIT(&s->used);
|
||||
QTAILQ_INIT(&s->free);
|
||||
|
||||
node = mlist_new(s->start, s->end - s->start);
|
||||
QTAILQ_INSERT_HEAD(&s->free, node, MLIST_ENTNAME);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,26 @@
|
|||
#include <inttypes.h>
|
||||
#include <glib.h>
|
||||
|
||||
typedef QTAILQ_HEAD(MemList, MemBlock) MemList;
|
||||
|
||||
typedef struct MemBlock {
|
||||
QTAILQ_ENTRY(MemBlock) MLIST_ENTNAME;
|
||||
uint64_t size;
|
||||
uint64_t addr;
|
||||
} MemBlock;
|
||||
|
||||
struct QGuestAllocator {
|
||||
QAllocOpts opts;
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
uint32_t page_size;
|
||||
|
||||
MemList used;
|
||||
MemList free;
|
||||
};
|
||||
|
||||
#define DEFAULT_PAGE_SIZE 4096
|
||||
|
||||
static void mlist_delete(MemList *list, MemBlock *node)
|
||||
{
|
||||
g_assert(list && node);
|
||||
|
@ -103,6 +123,21 @@ static void mlist_coalesce(MemList *head, MemBlock *node)
|
|||
} while (merge);
|
||||
}
|
||||
|
||||
static MemBlock *mlist_new(uint64_t addr, uint64_t size)
|
||||
{
|
||||
MemBlock *block;
|
||||
|
||||
if (!size) {
|
||||
return NULL;
|
||||
}
|
||||
block = g_malloc0(sizeof(MemBlock));
|
||||
|
||||
block->addr = addr;
|
||||
block->size = size;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
static uint64_t mlist_fulfill(QGuestAllocator *s, MemBlock *freenode,
|
||||
uint64_t size)
|
||||
{
|
||||
|
@ -187,21 +222,6 @@ static void mlist_free(QGuestAllocator *s, uint64_t addr)
|
|||
mlist_coalesce(&s->free, node);
|
||||
}
|
||||
|
||||
MemBlock *mlist_new(uint64_t addr, uint64_t size)
|
||||
{
|
||||
MemBlock *block;
|
||||
|
||||
if (!size) {
|
||||
return NULL;
|
||||
}
|
||||
block = g_malloc0(sizeof(MemBlock));
|
||||
|
||||
block->addr = addr;
|
||||
block->size = size;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mostly for valgrind happiness, but it does offer
|
||||
* a chokepoint for debugging guest memory leaks, too.
|
||||
|
@ -268,3 +288,44 @@ void guest_free(QGuestAllocator *allocator, uint64_t addr)
|
|||
mlist_check(allocator);
|
||||
}
|
||||
}
|
||||
|
||||
QGuestAllocator *alloc_init(uint64_t start, uint64_t end)
|
||||
{
|
||||
QGuestAllocator *s = g_malloc0(sizeof(*s));
|
||||
MemBlock *node;
|
||||
|
||||
s->start = start;
|
||||
s->end = end;
|
||||
|
||||
QTAILQ_INIT(&s->used);
|
||||
QTAILQ_INIT(&s->free);
|
||||
|
||||
node = mlist_new(s->start, s->end - s->start);
|
||||
QTAILQ_INSERT_HEAD(&s->free, node, MLIST_ENTNAME);
|
||||
|
||||
s->page_size = DEFAULT_PAGE_SIZE;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
QGuestAllocator *alloc_init_flags(QAllocOpts opts,
|
||||
uint64_t start, uint64_t end)
|
||||
{
|
||||
QGuestAllocator *s = alloc_init(start, end);
|
||||
s->opts = opts;
|
||||
return s;
|
||||
}
|
||||
|
||||
void alloc_set_page_size(QGuestAllocator *allocator, size_t page_size)
|
||||
{
|
||||
/* Can't alter the page_size for an allocator in-use */
|
||||
g_assert(QTAILQ_EMPTY(&allocator->used));
|
||||
|
||||
g_assert(is_power_of_2(page_size));
|
||||
allocator->page_size = page_size;
|
||||
}
|
||||
|
||||
void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts)
|
||||
{
|
||||
allocator->opts |= opts;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
#include <sys/types.h>
|
||||
#include "qemu/queue.h"
|
||||
|
||||
#define MLIST_ENTNAME entries
|
||||
|
||||
typedef enum {
|
||||
ALLOC_NO_FLAGS = 0x00,
|
||||
ALLOC_LEAK_WARN = 0x01,
|
||||
|
@ -26,28 +24,18 @@ typedef enum {
|
|||
ALLOC_PARANOID = 0x04
|
||||
} QAllocOpts;
|
||||
|
||||
typedef QTAILQ_HEAD(MemList, MemBlock) MemList;
|
||||
typedef struct MemBlock {
|
||||
QTAILQ_ENTRY(MemBlock) MLIST_ENTNAME;
|
||||
uint64_t size;
|
||||
uint64_t addr;
|
||||
} MemBlock;
|
||||
typedef struct QGuestAllocator QGuestAllocator;
|
||||
|
||||
typedef struct QGuestAllocator {
|
||||
QAllocOpts opts;
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
uint32_t page_size;
|
||||
|
||||
MemList used;
|
||||
MemList free;
|
||||
} QGuestAllocator;
|
||||
|
||||
MemBlock *mlist_new(uint64_t addr, uint64_t size);
|
||||
void alloc_uninit(QGuestAllocator *allocator);
|
||||
|
||||
/* Always returns page aligned values */
|
||||
uint64_t guest_alloc(QGuestAllocator *allocator, size_t size);
|
||||
void guest_free(QGuestAllocator *allocator, uint64_t addr);
|
||||
|
||||
QGuestAllocator *alloc_init(uint64_t start, uint64_t end);
|
||||
QGuestAllocator *alloc_init_flags(QAllocOpts flags,
|
||||
uint64_t start, uint64_t end);
|
||||
void alloc_set_page_size(QGuestAllocator *allocator, size_t page_size);
|
||||
void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
QA output created by 016
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||
|
||||
== reading at EOF ==
|
||||
read 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== reading far past EOF ==
|
||||
read 512/512 bytes at offset 268435456
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== writing at EOF ==
|
||||
wrote 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== writing far past EOF ==
|
||||
wrote 512/512 bytes at offset 268435456
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 268435456
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
*** done
|
|
@ -93,6 +93,7 @@ echo
|
|||
run_qemu -drive file="$TEST_IMG",format=foo
|
||||
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 === Overriding backing file ===
|
||||
|
|
|
@ -5,43 +5,46 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR
|
|||
=== Unknown option ===
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
|
||||
|
||||
|
||||
=== Unknown protocol option ===
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Block protocol 'file' doesn't support the option 'unknown_opt'
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=: Block protocol 'file' doesn't support the option 'unknown_opt'
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=on
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Block protocol 'file' doesn't support the option 'unknown_opt'
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=on: Block protocol 'file' doesn't support the option 'unknown_opt'
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=1234
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Block protocol 'file' doesn't support the option 'unknown_opt'
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=1234: Block protocol 'file' doesn't support the option 'unknown_opt'
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=foo
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Block protocol 'file' doesn't support the option 'unknown_opt'
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=foo: Block protocol 'file' doesn't support the option 'unknown_opt'
|
||||
|
||||
|
||||
=== Invalid format ===
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=foo
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=foo: 'foo' invalid format
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=foo: Unknown driver 'foo'
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,driver=foo
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=foo: could not open disk image TEST_DIR/t.qcow2: Unknown driver 'foo'
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=foo: Unknown driver 'foo'
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,driver=raw,format=qcow2
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=raw,format=qcow2: could not open disk image TEST_DIR/t.qcow2: Driver specified twice
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=raw,format=qcow2: Cannot specify both 'driver' and 'format'
|
||||
|
||||
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'
|
||||
|
||||
|
||||
=== Overriding backing file ===
|
||||
|
@ -55,13 +58,13 @@ ide0-hd0: TEST_DIR/t.qcow2 (qcow2)
|
|||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig: could not open disk image TEST_DIR/t.qcow2: Driver doesn't support backing files
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig: Driver doesn't support backing files
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,file.backing.driver=file,file.backing.filename=TEST_DIR/t.qcow2.orig
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=file,file.backing.filename=TEST_DIR/t.qcow2.orig: could not open disk image TEST_DIR/t.qcow2: Driver doesn't support backing files
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=file,file.backing.filename=TEST_DIR/t.qcow2.orig: Driver doesn't support backing files
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,file.backing.driver=qcow2,file.backing.file.filename=TEST_DIR/t.qcow2.orig
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=qcow2,file.backing.file.filename=TEST_DIR/t.qcow2.orig: could not open disk image TEST_DIR/t.qcow2: Driver doesn't support backing files
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=qcow2,file.backing.file.filename=TEST_DIR/t.qcow2.orig: Driver doesn't support backing files
|
||||
|
||||
|
||||
=== Enable and disable lazy refcounting on the command line, plus some invalid values ===
|
||||
|
@ -75,20 +78,20 @@ QEMU X.Y.Z monitor - type 'help' for more information
|
|||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off'
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: Parameter 'lazy-refcounts' expects 'on' or 'off'
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off'
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42: Parameter 'lazy-refcounts' expects 'on' or 'off'
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off'
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo: Parameter 'lazy-refcounts' expects 'on' or 'off'
|
||||
|
||||
|
||||
=== With version 2 images enabling lazy refcounts must fail ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: could not open disk image TEST_DIR/t.qcow2: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
|
@ -248,31 +251,31 @@ QEMU X.Y.Z monitor - type 'help' for more information
|
|||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device '' doesn't support the option 'filename'
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: Block format 'qcow2' used by device '' doesn't support the option 'filename'
|
||||
|
||||
|
||||
=== Leaving out required options ===
|
||||
|
||||
Testing: -drive driver=file
|
||||
QEMU_PROG: -drive driver=file: could not open disk image ide0-hd0: The 'file' block driver requires a file name
|
||||
QEMU_PROG: -drive driver=file: The 'file' block driver requires a file name
|
||||
|
||||
Testing: -drive driver=nbd
|
||||
QEMU_PROG: -drive driver=nbd: could not open disk image ide0-hd0: one of path and host must be specified.
|
||||
QEMU_PROG: -drive driver=nbd: one of path and host must be specified.
|
||||
|
||||
Testing: -drive driver=raw
|
||||
QEMU_PROG: -drive driver=raw: could not open disk image ide0-hd0: Can't use 'raw' as a block driver for the protocol level
|
||||
QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level
|
||||
|
||||
Testing: -drive file.driver=file
|
||||
QEMU_PROG: -drive file.driver=file: could not open disk image ide0-hd0: The 'file' block driver requires a file name
|
||||
QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name
|
||||
|
||||
Testing: -drive file.driver=nbd
|
||||
QEMU_PROG: -drive file.driver=nbd: could not open disk image ide0-hd0: one of path and host must be specified.
|
||||
QEMU_PROG: -drive file.driver=nbd: one of path and host must be specified.
|
||||
|
||||
Testing: -drive file.driver=raw
|
||||
QEMU_PROG: -drive file.driver=raw: could not open disk image ide0-hd0: Can't use 'raw' as a block driver for the protocol level
|
||||
QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level
|
||||
|
||||
Testing: -drive foo=bar
|
||||
QEMU_PROG: -drive foo=bar: could not open disk image ide0-hd0: Must specify either driver or file
|
||||
QEMU_PROG: -drive foo=bar: Must specify either driver or file
|
||||
|
||||
|
||||
=== Specifying both an option and its legacy alias ===
|
||||
|
@ -323,13 +326,13 @@ QEMU_PROG: -drive file=TEST_DIR/t.qcow2,readonly=on,read-only=off: 'read-only' a
|
|||
=== Parsing protocol from file name ===
|
||||
|
||||
Testing: -hda foo:bar
|
||||
QEMU_PROG: -hda foo:bar: could not open disk image foo:bar: Unknown protocol
|
||||
QEMU_PROG: -hda foo:bar: Unknown protocol 'foo'
|
||||
|
||||
Testing: -drive file=foo:bar
|
||||
QEMU_PROG: -drive file=foo:bar: could not open disk image foo:bar: Unknown protocol
|
||||
QEMU_PROG: -drive file=foo:bar: Unknown protocol 'foo'
|
||||
|
||||
Testing: -drive file.filename=foo:bar
|
||||
QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: Could not open 'foo:bar': No such file or directory
|
||||
QEMU_PROG: -drive file.filename=foo:bar: Could not open 'foo:bar': No such file or directory
|
||||
|
||||
Testing: -hda file:TEST_DIR/t.qcow2
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
|
@ -340,7 +343,7 @@ QEMU X.Y.Z monitor - type 'help' for more information
|
|||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file.filename=file:TEST_DIR/t.qcow2
|
||||
QEMU_PROG: -drive file.filename=file:TEST_DIR/t.qcow2: could not open disk image ide0-hd0: Could not open 'file:TEST_DIR/t.qcow2': No such file or directory
|
||||
QEMU_PROG: -drive file.filename=file:TEST_DIR/t.qcow2: Could not open 'file:TEST_DIR/t.qcow2': No such file or directory
|
||||
|
||||
|
||||
=== Snapshot mode ===
|
||||
|
|
|
@ -21,9 +21,9 @@ QMP_VERSION
|
|||
{"return": {}}
|
||||
{"error": {"class": "GenericError", "desc": "Device with id 'disk' already exists"}}
|
||||
{"error": {"class": "GenericError", "desc": "Device name 'test-node' conflicts with an existing node name"}}
|
||||
{"error": {"class": "GenericError", "desc": "could not open disk image disk2: node-name=disk is conflicting with a device id"}}
|
||||
{"error": {"class": "GenericError", "desc": "could not open disk image disk2: Duplicate node name"}}
|
||||
{"error": {"class": "GenericError", "desc": "could not open disk image disk3: node-name=disk3 is conflicting with a device id"}}
|
||||
{"error": {"class": "GenericError", "desc": "node-name=disk is conflicting with a device id"}}
|
||||
{"error": {"class": "GenericError", "desc": "Duplicate node name"}}
|
||||
{"error": {"class": "GenericError", "desc": "node-name=disk3 is conflicting with a device id"}}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
|
||||
|
@ -57,7 +57,7 @@ QMP_VERSION
|
|||
Testing:
|
||||
QMP_VERSION
|
||||
{"return": {}}
|
||||
{"error": {"class": "GenericError", "desc": "could not open disk image disk: Guest must be stopped for opening of encrypted image"}}
|
||||
{"error": {"class": "GenericError", "desc": "Guest must be stopped for opening of encrypted image"}}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Tests for IO throttling
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
import iotests
|
||||
|
||||
class ThrottleTestCase(iotests.QMPTestCase):
|
||||
test_img = "null-aio://"
|
||||
|
||||
def blockstats(self, device):
|
||||
result = self.vm.qmp("query-blockstats")
|
||||
for r in result['return']:
|
||||
if r['device'] == device:
|
||||
stat = r['stats']
|
||||
return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
|
||||
raise Exception("Device not found for blockstats: %s" % device)
|
||||
|
||||
def setUp(self):
|
||||
self.vm = iotests.VM().add_drive(self.test_img)
|
||||
self.vm.launch()
|
||||
|
||||
def tearDown(self):
|
||||
self.vm.shutdown()
|
||||
|
||||
def do_test_throttle(self, seconds, params):
|
||||
def check_limit(limit, num):
|
||||
# IO throttling algorithm is discrete, allow 10% error so the test
|
||||
# is more robust
|
||||
return limit == 0 or \
|
||||
(num < seconds * limit * 1.1
|
||||
and num > seconds * limit * 0.9)
|
||||
|
||||
nsec_per_sec = 1000000000
|
||||
|
||||
params['device'] = 'drive0'
|
||||
|
||||
result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params)
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
# Set vm clock to a known value
|
||||
ns = seconds * nsec_per_sec
|
||||
self.vm.qtest("clock_step %d" % ns)
|
||||
|
||||
# Submit enough requests. They will drain bps_max and iops_max, but the
|
||||
# rest requests won't get executed until we advance the virtual clock
|
||||
# with qtest interface
|
||||
rq_size = 512
|
||||
rd_nr = max(params['bps'] / rq_size / 2,
|
||||
params['bps_rd'] / rq_size,
|
||||
params['iops'] / 2,
|
||||
params['iops_rd'])
|
||||
rd_nr *= seconds * 2
|
||||
wr_nr = max(params['bps'] / rq_size / 2,
|
||||
params['bps_wr'] / rq_size,
|
||||
params['iops'] / 2,
|
||||
params['iops_wr'])
|
||||
wr_nr *= seconds * 2
|
||||
for i in range(rd_nr):
|
||||
self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % (i * rq_size, rq_size))
|
||||
for i in range(wr_nr):
|
||||
self.vm.hmp_qemu_io("drive0", "aio_write %d %d" % (i * rq_size, rq_size))
|
||||
|
||||
start_rd_bytes, start_rd_iops, start_wr_bytes, start_wr_iops = self.blockstats('drive0')
|
||||
|
||||
self.vm.qtest("clock_step %d" % ns)
|
||||
end_rd_bytes, end_rd_iops, end_wr_bytes, end_wr_iops = self.blockstats('drive0')
|
||||
|
||||
rd_bytes = end_rd_bytes - start_rd_bytes
|
||||
rd_iops = end_rd_iops - start_rd_iops
|
||||
wr_bytes = end_wr_bytes - start_wr_bytes
|
||||
wr_iops = end_wr_iops - start_wr_iops
|
||||
|
||||
self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes))
|
||||
self.assertTrue(check_limit(params['bps_rd'], rd_bytes))
|
||||
self.assertTrue(check_limit(params['bps_wr'], wr_bytes))
|
||||
self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops))
|
||||
self.assertTrue(check_limit(params['iops_rd'], rd_iops))
|
||||
self.assertTrue(check_limit(params['iops_wr'], wr_iops))
|
||||
|
||||
def test_all(self):
|
||||
params = {"bps": 4096,
|
||||
"bps_rd": 4096,
|
||||
"bps_wr": 4096,
|
||||
"iops": 10,
|
||||
"iops_rd": 10,
|
||||
"iops_wr": 10,
|
||||
}
|
||||
# Pick each out of all possible params and test
|
||||
for tk in params:
|
||||
limits = dict([(k, 0) for k in params])
|
||||
limits[tk] = params[tk]
|
||||
self.do_test_throttle(5, limits)
|
||||
|
||||
class ThrottleTestCoroutine(ThrottleTestCase):
|
||||
test_img = "null-co://"
|
||||
|
||||
if __name__ == '__main__':
|
||||
iotests.main(supported_fmts=["raw"])
|
|
@ -0,0 +1,5 @@
|
|||
..
|
||||
----------------------------------------------------------------------
|
||||
Ran 2 tests
|
||||
|
||||
OK
|
|
@ -0,0 +1,81 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Test case for drive-mirror to NBD (especially bdrv_swap() on NBD BDS)
|
||||
#
|
||||
# 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!
|
||||
|
||||
trap "exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
. ./common.qemu
|
||||
|
||||
_supported_fmt generic
|
||||
_supported_proto nbd
|
||||
_supported_os Linux
|
||||
_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
|
||||
|
||||
_make_test_img 64M
|
||||
$QEMU_IMG create -f $IMGFMT "$TEST_DIR/source.$IMGFMT" 64M | _filter_img_create
|
||||
|
||||
_launch_qemu -drive if=none,id=src,file="$TEST_DIR/source.$IMGFMT",format=raw \
|
||||
-nodefaults
|
||||
|
||||
_send_qemu_cmd $QEMU_HANDLE \
|
||||
"{'execute': 'qmp_capabilities'}" \
|
||||
'return'
|
||||
|
||||
# 'format': 'nbd' is not actually "correct", but this is probably the only way
|
||||
# to test bdrv_swap() on an NBD BDS
|
||||
_send_qemu_cmd $QEMU_HANDLE \
|
||||
"{'execute': 'drive-mirror',
|
||||
'arguments': {'device': 'src',
|
||||
'target': '$TEST_IMG',
|
||||
'format': 'nbd',
|
||||
'sync':'full',
|
||||
'mode':'existing'}}" \
|
||||
'BLOCK_JOB_READY'
|
||||
|
||||
_send_qemu_cmd $QEMU_HANDLE \
|
||||
"{'execute': 'block-job-complete',
|
||||
'arguments': {'device': 'src'}}" \
|
||||
'BLOCK_JOB_COMPLETE'
|
||||
|
||||
_send_qemu_cmd $QEMU_HANDLE \
|
||||
"{'execute': 'quit'}" \
|
||||
'return'
|
||||
|
||||
wait=1 _cleanup_qemu
|
||||
|
||||
_cleanup_test_img
|
||||
rm -f "$TEST_DIR/source.$IMGFMT"
|
||||
|
||||
# success, all done
|
||||
echo '*** done'
|
||||
rm -f $seq.full
|
||||
status=0
|
|
@ -0,0 +1,11 @@
|
|||
QA output created by 094
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
Formatting 'TEST_DIR/source.IMGFMT', fmt=IMGFMT size=67108864
|
||||
{"return": {}}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
|
||||
*** done
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Test I/O after EOF for growable images.
|
||||
# Test case for qemu-img convert to NBD
|
||||
#
|
||||
# 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
|
||||
|
@ -19,18 +19,19 @@
|
|||
#
|
||||
|
||||
# creator
|
||||
owner=hch@lst.de
|
||||
owner=mreitz@redhat.com
|
||||
|
||||
seq=`basename $0`
|
||||
seq="$(basename $0)"
|
||||
echo "QA output created by $seq"
|
||||
|
||||
here=`pwd`
|
||||
here="$PWD"
|
||||
tmp=/tmp/$$
|
||||
status=1 # failure is the default!
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
_cleanup_test_img
|
||||
rm -f "$SRC_IMG"
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
|
@ -39,35 +40,23 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
|||
. ./common.filter
|
||||
|
||||
_supported_fmt raw
|
||||
_supported_proto file sheepdog nfs
|
||||
_supported_proto nbd
|
||||
_supported_os Linux
|
||||
|
||||
SRC_IMG="$TEST_DIR/source.$IMGFMT"
|
||||
|
||||
# No -f, use probing for the protocol driver
|
||||
QEMU_IO_PROTO="$QEMU_IO_PROG -g --cache $CACHEMODE"
|
||||
_make_test_img 1M
|
||||
$QEMU_IMG create -f $IMGFMT "$SRC_IMG" 1M | _filter_img_create
|
||||
|
||||
size=128M
|
||||
_make_test_img $size
|
||||
$QEMU_IO -c 'write -P 42 0 1M' "$SRC_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "== reading at EOF =="
|
||||
$QEMU_IO_PROTO -c "read -P 0 $size 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG convert -n -f $IMGFMT -O raw "$SRC_IMG" "$TEST_IMG"
|
||||
|
||||
echo
|
||||
echo "== reading far past EOF =="
|
||||
$QEMU_IO_PROTO -c "read -P 0 256M 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c 'read -P 42 0 1M' "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "== writing at EOF =="
|
||||
$QEMU_IO_PROTO -c "write -P 66 $size 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 66 $size 512" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "== writing far past EOF =="
|
||||
$QEMU_IO_PROTO -c "write -P 66 256M 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 66 256M 512" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
echo
|
||||
echo '*** done'
|
||||
rm -f $seq.full
|
||||
status=0
|
|
@ -0,0 +1,9 @@
|
|||
QA output created by 123
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
|
||||
Formatting 'TEST_DIR/source.IMGFMT', fmt=IMGFMT size=1048576
|
||||
wrote 1048576/1048576 bytes at offset 0
|
||||
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 1048576/1048576 bytes at offset 0
|
||||
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
*** done
|
|
@ -187,13 +187,23 @@ function _launch_qemu()
|
|||
|
||||
|
||||
# Silenty kills the QEMU process
|
||||
#
|
||||
# If $wait is set to anything other than the empty string, the process will not
|
||||
# be killed but only waited for, and any output will be forwarded to stdout. If
|
||||
# $wait is empty, the process will be killed and all output will be suppressed.
|
||||
function _cleanup_qemu()
|
||||
{
|
||||
# QEMU_PID[], QEMU_IN[], QEMU_OUT[] all use same indices
|
||||
for i in "${!QEMU_OUT[@]}"
|
||||
do
|
||||
kill -KILL ${QEMU_PID[$i]} 2>/dev/null
|
||||
if [ -z "${wait}" ]; then
|
||||
kill -KILL ${QEMU_PID[$i]} 2>/dev/null
|
||||
fi
|
||||
wait ${QEMU_PID[$i]} 2>/dev/null # silent kill
|
||||
if [ -n "${wait}" ]; then
|
||||
cat <&${QEMU_OUT[$i]} | _filter_testdir | _filter_qemu \
|
||||
| _filter_qemu_io | _filter_qmp
|
||||
fi
|
||||
rm -f "${QEMU_FIFO_IN}_${i}" "${QEMU_FIFO_OUT}_${i}"
|
||||
eval "exec ${QEMU_IN[$i]}<&-" # close file descriptors
|
||||
eval "exec ${QEMU_OUT[$i]}<&-"
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
013 rw auto
|
||||
014 rw auto
|
||||
015 rw snapshot auto
|
||||
016 rw auto quick
|
||||
# 016 was removed, do not reuse
|
||||
017 rw backing auto quick
|
||||
018 rw backing auto quick
|
||||
019 rw backing auto quick
|
||||
|
@ -99,6 +99,8 @@
|
|||
090 rw auto quick
|
||||
091 rw auto
|
||||
092 rw auto quick
|
||||
093 auto
|
||||
094 rw auto quick
|
||||
095 rw auto quick
|
||||
097 rw auto backing
|
||||
098 rw auto backing quick
|
||||
|
@ -117,3 +119,4 @@
|
|||
113 rw auto quick
|
||||
114 rw auto quick
|
||||
116 rw auto quick
|
||||
123 rw auto quick
|
||||
|
|
|
@ -21,8 +21,11 @@ import re
|
|||
import subprocess
|
||||
import string
|
||||
import unittest
|
||||
import sys; sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts', 'qmp'))
|
||||
import sys
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts'))
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts', 'qmp'))
|
||||
import qmp
|
||||
import qtest
|
||||
import struct
|
||||
|
||||
__all__ = ['imgfmt', 'imgproto', 'test_dir' 'qemu_img', 'qemu_io',
|
||||
|
@ -81,10 +84,12 @@ class VM(object):
|
|||
def __init__(self):
|
||||
self._monitor_path = os.path.join(test_dir, 'qemu-mon.%d' % os.getpid())
|
||||
self._qemu_log_path = os.path.join(test_dir, 'qemu-log.%d' % os.getpid())
|
||||
self._qtest_path = os.path.join(test_dir, 'qemu-qtest.%d' % os.getpid())
|
||||
self._args = qemu_args + ['-chardev',
|
||||
'socket,id=mon,path=' + self._monitor_path,
|
||||
'-mon', 'chardev=mon,mode=control',
|
||||
'-qtest', 'stdio', '-machine', 'accel=qtest',
|
||||
'-qtest', 'unix:path=' + self._qtest_path,
|
||||
'-machine', 'accel=qtest',
|
||||
'-display', 'none', '-vga', 'none']
|
||||
self._num_drives = 0
|
||||
|
||||
|
@ -160,9 +165,11 @@ class VM(object):
|
|||
qemulog = open(self._qemu_log_path, 'wb')
|
||||
try:
|
||||
self._qmp = qmp.QEMUMonitorProtocol(self._monitor_path, server=True)
|
||||
self._qtest = qtest.QEMUQtestProtocol(self._qtest_path, server=True)
|
||||
self._popen = subprocess.Popen(self._args, stdin=devnull, stdout=qemulog,
|
||||
stderr=subprocess.STDOUT)
|
||||
self._qmp.accept()
|
||||
self._qtest.accept()
|
||||
except:
|
||||
os.remove(self._monitor_path)
|
||||
raise
|
||||
|
@ -173,18 +180,26 @@ class VM(object):
|
|||
self._qmp.cmd('quit')
|
||||
self._popen.wait()
|
||||
os.remove(self._monitor_path)
|
||||
os.remove(self._qtest_path)
|
||||
os.remove(self._qemu_log_path)
|
||||
self._popen = None
|
||||
|
||||
underscore_to_dash = string.maketrans('_', '-')
|
||||
def qmp(self, cmd, **args):
|
||||
def qmp(self, cmd, conv_keys=True, **args):
|
||||
'''Invoke a QMP command and return the result dict'''
|
||||
qmp_args = dict()
|
||||
for k in args.keys():
|
||||
qmp_args[k.translate(self.underscore_to_dash)] = args[k]
|
||||
if conv_keys:
|
||||
qmp_args[k.translate(self.underscore_to_dash)] = args[k]
|
||||
else:
|
||||
qmp_args[k] = args[k]
|
||||
|
||||
return self._qmp.cmd(cmd, args=qmp_args)
|
||||
|
||||
def qtest(self, cmd):
|
||||
'''Send a qtest command to guest'''
|
||||
return self._qtest.cmd(cmd)
|
||||
|
||||
def get_qmp_event(self, wait=False):
|
||||
'''Poll for one queued QMP events and return it'''
|
||||
return self._qmp.pull_event(wait=wait)
|
||||
|
|
Loading…
Reference in New Issue