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:
Peter Maydell 2015-02-24 12:59:13 +00:00
commit 3dc10613c3
57 changed files with 3029 additions and 1547 deletions

73
block.c
View File

@ -508,9 +508,8 @@ int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
Error *local_err = NULL; Error *local_err = NULL;
int ret; int ret;
drv = bdrv_find_protocol(filename, true); drv = bdrv_find_protocol(filename, true, errp);
if (drv == NULL) { if (drv == NULL) {
error_setg(errp, "Could not find protocol for file '%s'", filename);
return -ENOENT; return -ENOENT;
} }
@ -628,7 +627,8 @@ static BlockDriver *find_hdev_driver(const char *filename)
} }
BlockDriver *bdrv_find_protocol(const char *filename, BlockDriver *bdrv_find_protocol(const char *filename,
bool allow_protocol_prefix) bool allow_protocol_prefix,
Error **errp)
{ {
BlockDriver *drv1; BlockDriver *drv1;
char protocol[128]; char protocol[128];
@ -666,6 +666,8 @@ BlockDriver *bdrv_find_protocol(const char *filename,
return drv1; return drv1;
} }
} }
error_setg(errp, "Unknown protocol '%s'", protocol);
return NULL; return NULL;
} }
@ -970,7 +972,6 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
bs->zero_beyond_eof = true; bs->zero_beyond_eof = true;
open_flags = bdrv_open_flags(bs, flags); open_flags = bdrv_open_flags(bs, flags);
bs->read_only = !(open_flags & BDRV_O_RDWR); 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)) { if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
error_setg(errp, error_setg(errp,
@ -1136,9 +1137,8 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
} else { } else {
if (!drvname && protocol) { if (!drvname && protocol) {
if (filename) { if (filename) {
drv = bdrv_find_protocol(filename, parse_filename); drv = bdrv_find_protocol(filename, parse_filename, errp);
if (!drv) { if (!drv) {
error_setg(errp, "Unknown protocol");
return -EINVAL; return -EINVAL;
} }
@ -1885,7 +1885,6 @@ void bdrv_close(BlockDriverState *bs)
bs->encrypted = 0; bs->encrypted = 0;
bs->valid_key = 0; bs->valid_key = 0;
bs->sg = 0; bs->sg = 0;
bs->growable = 0;
bs->zero_beyond_eof = false; bs->zero_beyond_eof = false;
QDECREF(bs->options); QDECREF(bs->options);
bs->options = NULL; bs->options = NULL;
@ -2645,25 +2644,17 @@ exit:
static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset, static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
size_t size) size_t size)
{ {
int64_t len;
if (size > BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS) { if (size > BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS) {
return -EIO; return -EIO;
} }
if (!bdrv_is_inserted(bs)) if (!bdrv_is_inserted(bs)) {
return -ENOMEDIUM; return -ENOMEDIUM;
}
if (bs->growable) if (offset < 0) {
return 0;
len = bdrv_getlength(bs);
if (offset < 0)
return -EIO;
if ((offset > len) || (len - offset < size))
return -EIO; return -EIO;
}
return 0; return 0;
} }
@ -3042,10 +3033,10 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs,
} }
/* Forward the request to the BlockDriver */ /* 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); ret = drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov);
} else { } else {
/* Read zeros after EOF of growable BDSes */ /* Read zeros after EOF */
int64_t total_sectors, max_nb_sectors; int64_t total_sectors, max_nb_sectors;
total_sectors = bdrv_nb_sectors(bs); total_sectors = bdrv_nb_sectors(bs);
@ -3107,8 +3098,10 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs,
if (!drv) { if (!drv) {
return -ENOMEDIUM; 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) { 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); 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); 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) { if (bs->read_only) {
return -EACCES; 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 */ /* 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) const uint8_t *buf, int nb_sectors)
{ {
BlockDriver *drv = bs->drv; BlockDriver *drv = bs->drv;
if (!drv) int ret;
if (!drv) {
return -ENOMEDIUM; return -ENOMEDIUM;
if (!drv->bdrv_write_compressed) }
if (!drv->bdrv_write_compressed) {
return -ENOTSUP; 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)); 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 coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
int nb_sectors) int nb_sectors)
{ {
int max_discard; int max_discard, ret;
if (!bs->drv) { if (!bs->drv) {
return -ENOMEDIUM; 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) { } else if (bs->read_only) {
return -EROFS; return -EROFS;
} }
@ -5623,9 +5627,8 @@ void bdrv_img_create(const char *filename, const char *fmt,
return; return;
} }
proto_drv = bdrv_find_protocol(filename, true); proto_drv = bdrv_find_protocol(filename, true, errp);
if (!proto_drv) { if (!proto_drv) {
error_setg(errp, "Unknown protocol '%s'", filename);
return; return;
} }

View File

@ -31,6 +31,16 @@ struct BlockBackend {
void *dev_opaque; 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); static void drive_info_del(DriveInfo *dinfo);
/* All the BlockBackends (except for hidden ones) */ /* All the BlockBackends (except for hidden ones) */
@ -91,6 +101,40 @@ BlockBackend *blk_new_with_bs(const char *name, Error **errp)
return blk; 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) static void blk_delete(BlockBackend *blk)
{ {
assert(!blk->refcnt); assert(!blk->refcnt);
@ -394,39 +438,137 @@ void blk_iostatus_enable(BlockBackend *blk)
bdrv_iostatus_enable(blk->bs); 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 blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf,
int nb_sectors) 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); return bdrv_read(blk->bs, sector_num, buf, nb_sectors);
} }
int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf, int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf,
int nb_sectors) 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); 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 blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf,
int nb_sectors) 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); 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, BlockAIOCB *blk_aio_write_zeroes(BlockBackend *blk, int64_t sector_num,
int nb_sectors, BdrvRequestFlags flags, int nb_sectors, BdrvRequestFlags flags,
BlockCompletionFunc *cb, void *opaque) 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, return bdrv_aio_write_zeroes(blk->bs, sector_num, nb_sectors, flags,
cb, opaque); cb, opaque);
} }
int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count) 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); return bdrv_pread(blk->bs, offset, buf, count);
} }
int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int 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); 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); 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, BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num,
QEMUIOVector *iov, int nb_sectors, QEMUIOVector *iov, int nb_sectors,
BlockCompletionFunc *cb, void *opaque) 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); 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, QEMUIOVector *iov, int nb_sectors,
BlockCompletionFunc *cb, void *opaque) 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); 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, int64_t sector_num, int nb_sectors,
BlockCompletionFunc *cb, void *opaque) 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); 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 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); 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 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); 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); 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);
}

View File

@ -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 */ /* finish any pending coroutines */
shutdown(client->sock, 2); shutdown(client->sock, 2);
nbd_recv_coroutines_enter_all(client); nbd_recv_coroutines_enter_all(client);
nbd_client_session_detach_aio_context(client); nbd_client_detach_aio_context(bs);
closesocket(client->sock); closesocket(client->sock);
client->sock = -1; client->sock = -1;
} }
static void nbd_reply_ready(void *opaque) static void nbd_reply_ready(void *opaque)
{ {
NbdClientSession *s = opaque; BlockDriverState *bs = opaque;
NbdClientSession *s = nbd_get_client_session(bs);
uint64_t i; uint64_t i;
int ret; int ret;
@ -89,28 +92,40 @@ static void nbd_reply_ready(void *opaque)
} }
fail: fail:
nbd_teardown_connection(s); nbd_teardown_connection(bs);
} }
static void nbd_restart_write(void *opaque) 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, static int nbd_co_send_request(BlockDriverState *bs,
struct nbd_request *request, struct nbd_request *request,
QEMUIOVector *qiov, int offset) QEMUIOVector *qiov, int offset)
{ {
NbdClientSession *s = nbd_get_client_session(bs);
AioContext *aio_context; AioContext *aio_context;
int rc, ret; int rc, ret, i;
qemu_co_mutex_lock(&s->send_mutex); 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(); 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, 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 (qiov) {
if (!s->is_unix) { if (!s->is_unix) {
socket_set_cork(s->sock, 1); socket_set_cork(s->sock, 1);
@ -129,7 +144,7 @@ static int nbd_co_send_request(NbdClientSession *s,
} else { } else {
rc = nbd_send_request(s->sock, request); 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; s->send_coroutine = NULL;
qemu_co_mutex_unlock(&s->send_mutex); qemu_co_mutex_unlock(&s->send_mutex);
return rc; return rc;
@ -164,8 +179,6 @@ static void nbd_co_receive_reply(NbdClientSession *s,
static void nbd_coroutine_start(NbdClientSession *s, static void nbd_coroutine_start(NbdClientSession *s,
struct nbd_request *request) struct nbd_request *request)
{ {
int i;
/* Poor man semaphore. The free_sema is locked when no other request /* Poor man semaphore. The free_sema is locked when no other request
* can be accepted, and unlocked after receiving one reply. */ * can be accepted, and unlocked after receiving one reply. */
if (s->in_flight >= MAX_NBD_REQUESTS - 1) { if (s->in_flight >= MAX_NBD_REQUESTS - 1) {
@ -174,15 +187,7 @@ static void nbd_coroutine_start(NbdClientSession *s,
} }
s->in_flight++; s->in_flight++;
for (i = 0; i < MAX_NBD_REQUESTS; i++) { /* s->recv_coroutine[i] is set as soon as we get the send_lock. */
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);
} }
static void nbd_coroutine_end(NbdClientSession *s, 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 nb_sectors, QEMUIOVector *qiov,
int offset) int offset)
{ {
NbdClientSession *client = nbd_get_client_session(bs);
struct nbd_request request = { .type = NBD_CMD_READ }; struct nbd_request request = { .type = NBD_CMD_READ };
struct nbd_reply reply; struct nbd_reply reply;
ssize_t ret; ssize_t ret;
@ -207,7 +213,7 @@ static int nbd_co_readv_1(NbdClientSession *client, int64_t sector_num,
request.len = nb_sectors * 512; request.len = nb_sectors * 512;
nbd_coroutine_start(client, &request); 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) { if (ret < 0) {
reply.error = -ret; reply.error = -ret;
} else { } 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 nb_sectors, QEMUIOVector *qiov,
int offset) int offset)
{ {
NbdClientSession *client = nbd_get_client_session(bs);
struct nbd_request request = { .type = NBD_CMD_WRITE }; struct nbd_request request = { .type = NBD_CMD_WRITE };
struct nbd_reply reply; struct nbd_reply reply;
ssize_t ret; ssize_t ret;
if (!bdrv_enable_write_cache(client->bs) && if (!bdrv_enable_write_cache(bs) &&
(client->nbdflags & NBD_FLAG_SEND_FUA)) { (client->nbdflags & NBD_FLAG_SEND_FUA)) {
request.type |= NBD_CMD_FLAG_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; request.len = nb_sectors * 512;
nbd_coroutine_start(client, &request); 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) { if (ret < 0) {
reply.error = -ret; reply.error = -ret;
} else { } else {
@ -249,14 +256,13 @@ static int nbd_co_writev_1(NbdClientSession *client, int64_t sector_num,
* remain aligned to 4K. */ * remain aligned to 4K. */
#define NBD_MAX_SECTORS 2040 #define NBD_MAX_SECTORS 2040
int nbd_client_session_co_readv(NbdClientSession *client, int64_t sector_num, int nbd_client_co_readv(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov) int nb_sectors, QEMUIOVector *qiov)
{ {
int offset = 0; int offset = 0;
int ret; int ret;
while (nb_sectors > NBD_MAX_SECTORS) { while (nb_sectors > NBD_MAX_SECTORS) {
ret = nbd_co_readv_1(client, sector_num, ret = nbd_co_readv_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset);
NBD_MAX_SECTORS, qiov, offset);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -264,17 +270,16 @@ int nbd_client_session_co_readv(NbdClientSession *client, int64_t sector_num,
sector_num += NBD_MAX_SECTORS; sector_num += NBD_MAX_SECTORS;
nb_sectors -= 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 nbd_client_co_writev(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov) int nb_sectors, QEMUIOVector *qiov)
{ {
int offset = 0; int offset = 0;
int ret; int ret;
while (nb_sectors > NBD_MAX_SECTORS) { while (nb_sectors > NBD_MAX_SECTORS) {
ret = nbd_co_writev_1(client, sector_num, ret = nbd_co_writev_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset);
NBD_MAX_SECTORS, qiov, offset);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -282,11 +287,12 @@ int nbd_client_session_co_writev(NbdClientSession *client, int64_t sector_num,
sector_num += NBD_MAX_SECTORS; sector_num += NBD_MAX_SECTORS;
nb_sectors -= 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_request request = { .type = NBD_CMD_FLUSH };
struct nbd_reply reply; struct nbd_reply reply;
ssize_t ret; ssize_t ret;
@ -303,7 +309,7 @@ int nbd_client_session_co_flush(NbdClientSession *client)
request.len = 0; request.len = 0;
nbd_coroutine_start(client, &request); 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) { if (ret < 0) {
reply.error = -ret; reply.error = -ret;
} else { } else {
@ -313,9 +319,10 @@ int nbd_client_session_co_flush(NbdClientSession *client)
return -reply.error; return -reply.error;
} }
int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num, int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num,
int nb_sectors) int nb_sectors)
{ {
NbdClientSession *client = nbd_get_client_session(bs);
struct nbd_request request = { .type = NBD_CMD_TRIM }; struct nbd_request request = { .type = NBD_CMD_TRIM };
struct nbd_reply reply; struct nbd_reply reply;
ssize_t ret; ssize_t ret;
@ -327,7 +334,7 @@ int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num,
request.len = nb_sectors * 512; request.len = nb_sectors * 512;
nbd_coroutine_start(client, &request); 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) { if (ret < 0) {
reply.error = -ret; reply.error = -ret;
} else { } 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, aio_set_fd_handler(bdrv_get_aio_context(bs),
NULL, NULL, NULL); nbd_get_client_session(bs)->sock, NULL, NULL, NULL);
} }
void nbd_client_session_attach_aio_context(NbdClientSession *client, void nbd_client_attach_aio_context(BlockDriverState *bs,
AioContext *new_context) AioContext *new_context)
{ {
aio_set_fd_handler(new_context, client->sock, aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sock,
nbd_reply_ready, NULL, client); 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 = { struct nbd_request request = {
.type = NBD_CMD_DISC, .type = NBD_CMD_DISC,
.from = 0, .from = 0,
.len = 0 .len = 0
}; };
if (!client->bs) {
return;
}
if (client->sock == -1) { if (client->sock == -1) {
return; return;
} }
nbd_send_request(client->sock, &request); nbd_send_request(client->sock, &request);
nbd_teardown_connection(client); nbd_teardown_connection(bs);
client->bs = NULL;
} }
int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs, int nbd_client_init(BlockDriverState *bs, int sock, const char *export,
int sock, const char *export, Error **errp) Error **errp)
{ {
NbdClientSession *client = nbd_get_client_session(bs);
int ret; int ret;
/* NBD handshake */ /* 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->send_mutex);
qemu_co_mutex_init(&client->free_sema); qemu_co_mutex_init(&client->free_sema);
client->bs = bs;
client->sock = sock; client->sock = sock;
/* Now that we're connected, set the socket to be non-blocking and /* Now that we're connected, set the socket to be non-blocking and
* kick the reply mechanism. */ * kick the reply mechanism. */
qemu_set_nonblock(sock); 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"); logout("Established connection with NBD server\n");
return 0; return 0;

View File

@ -31,24 +31,24 @@ typedef struct NbdClientSession {
struct nbd_reply reply; struct nbd_reply reply;
bool is_unix; bool is_unix;
BlockDriverState *bs;
} NbdClientSession; } NbdClientSession;
int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs, NbdClientSession *nbd_get_client_session(BlockDriverState *bs);
int sock, const char *export_name, Error **errp);
void nbd_client_session_close(NbdClientSession *client);
int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num, int nbd_client_init(BlockDriverState *bs, int sock, const char *export_name,
int nb_sectors); Error **errp);
int nbd_client_session_co_flush(NbdClientSession *client); void nbd_client_close(BlockDriverState *bs);
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);
void nbd_client_session_detach_aio_context(NbdClientSession *client); int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num,
void nbd_client_session_attach_aio_context(NbdClientSession *client, int nb_sectors);
AioContext *new_context); 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 */ #endif /* NBD_CLIENT_H */

View File

@ -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) static int nbd_establish_connection(BlockDriverState *bs, Error **errp)
{ {
BDRVNBDState *s = bs->opaque; BDRVNBDState *s = bs->opaque;
@ -271,7 +277,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
} }
/* NBD handshake */ /* NBD handshake */
result = nbd_client_session_init(&s->client, bs, sock, export, errp); result = nbd_client_init(bs, sock, export, errp);
g_free(export); g_free(export);
return result; 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, static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov) int nb_sectors, QEMUIOVector *qiov)
{ {
BDRVNBDState *s = bs->opaque; return nbd_client_co_readv(bs, sector_num, nb_sectors, qiov);
return nbd_client_session_co_readv(&s->client, sector_num,
nb_sectors, qiov);
} }
static int nbd_co_writev(BlockDriverState *bs, int64_t sector_num, static int nbd_co_writev(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov) int nb_sectors, QEMUIOVector *qiov)
{ {
BDRVNBDState *s = bs->opaque; return nbd_client_co_writev(bs, sector_num, nb_sectors, qiov);
return nbd_client_session_co_writev(&s->client, sector_num,
nb_sectors, qiov);
} }
static int nbd_co_flush(BlockDriverState *bs) static int nbd_co_flush(BlockDriverState *bs)
{ {
BDRVNBDState *s = bs->opaque; return nbd_client_co_flush(bs);
return nbd_client_session_co_flush(&s->client);
} }
static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) 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, static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num,
int nb_sectors) int nb_sectors)
{ {
BDRVNBDState *s = bs->opaque; return nbd_client_co_discard(bs, sector_num, nb_sectors);
return nbd_client_session_co_discard(&s->client, sector_num,
nb_sectors);
} }
static void nbd_close(BlockDriverState *bs) static void nbd_close(BlockDriverState *bs)
@ -321,7 +316,7 @@ static void nbd_close(BlockDriverState *bs)
BDRVNBDState *s = bs->opaque; BDRVNBDState *s = bs->opaque;
qemu_opts_del(s->socket_opts); qemu_opts_del(s->socket_opts);
nbd_client_session_close(&s->client); nbd_client_close(bs);
} }
static int64_t nbd_getlength(BlockDriverState *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) static void nbd_detach_aio_context(BlockDriverState *bs)
{ {
BDRVNBDState *s = bs->opaque; nbd_client_detach_aio_context(bs);
nbd_client_session_detach_aio_context(&s->client);
} }
static void nbd_attach_aio_context(BlockDriverState *bs, static void nbd_attach_aio_context(BlockDriverState *bs,
AioContext *new_context) AioContext *new_context)
{ {
BDRVNBDState *s = bs->opaque; nbd_client_attach_aio_context(bs, new_context);
nbd_client_session_attach_aio_context(&s->client, new_context);
} }
static void nbd_refresh_filename(BlockDriverState *bs) static void nbd_refresh_filename(BlockDriverState *bs)

View File

@ -2521,15 +2521,12 @@ static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
int64_t total_sectors = bs->total_sectors; int64_t total_sectors = bs->total_sectors;
int growable = bs->growable;
bool zero_beyond_eof = bs->zero_beyond_eof; bool zero_beyond_eof = bs->zero_beyond_eof;
int ret; int ret;
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE); BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE);
bs->growable = 1;
bs->zero_beyond_eof = false; bs->zero_beyond_eof = false;
ret = bdrv_pwritev(bs, qcow2_vm_state_offset(s) + pos, qiov); ret = bdrv_pwritev(bs, qcow2_vm_state_offset(s) + pos, qiov);
bs->growable = growable;
bs->zero_beyond_eof = zero_beyond_eof; bs->zero_beyond_eof = zero_beyond_eof;
/* bdrv_co_do_writev will have increased the total_sectors value to include /* 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) int64_t pos, int size)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
int growable = bs->growable;
bool zero_beyond_eof = bs->zero_beyond_eof; bool zero_beyond_eof = bs->zero_beyond_eof;
int ret; int ret;
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_LOAD); BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_LOAD);
bs->growable = 1;
bs->zero_beyond_eof = false; bs->zero_beyond_eof = false;
ret = bdrv_pread(bs, qcow2_vm_state_offset(s) + pos, buf, size); ret = bdrv_pread(bs, qcow2_vm_state_offset(s) + pos, buf, size);
bs->growable = growable;
bs->zero_beyond_eof = zero_beyond_eof; bs->zero_beyond_eof = zero_beyond_eof;
return ret; return ret;

View File

@ -1047,7 +1047,7 @@ static int aio_worker(void *arg)
switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) { switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) {
case QEMU_AIO_READ: case QEMU_AIO_READ:
ret = handle_aiocb_rw(aiocb); 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, iov_memset(aiocb->aio_iov, aiocb->aio_niov, ret,
0, aiocb->aio_nbytes - ret); 0, aiocb->aio_nbytes - ret);

View File

@ -101,7 +101,7 @@ static int aio_worker(void *arg)
switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) { switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) {
case QEMU_AIO_READ: case QEMU_AIO_READ:
count = handle_aiocb_rw(aiocb); 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 /* A short read means that we have reached EOF. Pad the buffer
* with zeros for bytes after EOF. */ * with zeros for bytes after EOF. */
iov_memset(aiocb->aio_iov, aiocb->aio_niov, count, iov_memset(aiocb->aio_iov, aiocb->aio_niov, count,

View File

@ -1730,7 +1730,7 @@ static int sd_create(const char *filename, QemuOpts *opts,
BlockDriver *drv; BlockDriver *drv;
/* Currently, only Sheepdog backing image is supported. */ /* 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) { if (!drv || strcmp(drv->protocol_name, "sheepdog") != 0) {
error_setg(errp, "backing_file must be a sheepdog image"); error_setg(errp, "backing_file must be a sheepdog image");
ret = -EINVAL; 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; int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE;
BDRVSheepdogState *s = bs->opaque; BDRVSheepdogState *s = bs->opaque;
if (bs->growable && offset > s->inode.vdi_size) { if (offset > s->inode.vdi_size) {
ret = sd_truncate(bs, offset); ret = sd_truncate(bs, offset);
if (ret < 0) { if (ret < 0) {
return ret; return ret;

View File

@ -843,8 +843,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
} }
extent_path = g_malloc0(PATH_MAX); extent_path = g_malloc0(PATH_MAX);
path_combine(extent_path, sizeof(extent_path), path_combine(extent_path, PATH_MAX, desc_file_path, fname);
desc_file_path, fname);
extent_file = NULL; extent_file = NULL;
ret = bdrv_open(&extent_file, extent_path, NULL, NULL, ret = bdrv_open(&extent_file, extent_path, NULL, NULL,
bs->open_flags | BDRV_O_PROTOCOL, NULL, errp); bs->open_flags | BDRV_O_PROTOCOL, NULL, errp);

View File

@ -354,13 +354,11 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
ThrottleConfig cfg; ThrottleConfig cfg;
int snapshot = 0; int snapshot = 0;
bool copy_on_read; bool copy_on_read;
int ret;
Error *error = NULL; Error *error = NULL;
QemuOpts *opts; QemuOpts *opts;
const char *id; const char *id;
bool has_driver_specific_opts; bool has_driver_specific_opts;
BlockdevDetectZeroesOptions detect_zeroes; BlockdevDetectZeroesOptions detect_zeroes;
BlockDriver *drv = NULL;
/* Check common options by copying from bs_opts to opts, all other options /* Check common options by copying from bs_opts to opts, all other options
* stay in bs_opts for processing by bdrv_open(). */ * 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; goto early_err;
} }
drv = bdrv_find_format(buf); if (qdict_haskey(bs_opts, "driver")) {
if (!drv) { error_setg(errp, "Cannot specify both 'driver' and 'format'");
error_setg(errp, "'%s' invalid format", buf);
goto early_err; goto early_err;
} }
qdict_put(bs_opts, "driver", qstring_from_str(buf));
} }
/* disk I/O throttling */ /* disk I/O throttling */
@ -505,13 +503,46 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
} }
/* init */ /* init */
blk = blk_new_with_bs(qemu_opts_id(opts), errp); if ((!file || !*file) && !has_driver_specific_opts) {
if (!blk) { blk = blk_new_with_bs(qemu_opts_id(opts), errp);
goto early_err; 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; bs->detect_zeroes = detect_zeroes;
bdrv_set_on_error(bs, on_read_error, on_write_error); 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); 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)) { if (bdrv_key_required(bs)) {
autostart = 0; autostart = 0;
} }
QDECREF(bs_opts); err_no_bs_opts:
qemu_opts_del(opts); qemu_opts_del(opts);
return blk; return blk;
err:
blk_unref(blk);
early_err: early_err:
qemu_opts_del(opts); qemu_opts_del(opts);
err_no_opts: err_no_opts:

4
cpus.c
View File

@ -361,15 +361,19 @@ static void icount_warp_rt(void *opaque)
void qtest_clock_warp(int64_t dest) void qtest_clock_warp(int64_t dest)
{ {
int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
AioContext *aio_context;
assert(qtest_enabled()); assert(qtest_enabled());
aio_context = qemu_get_aio_context();
while (clock < dest) { while (clock < dest) {
int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL); int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
int64_t warp = qemu_soonest_timeout(dest - clock, deadline); int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
seqlock_write_lock(&timers_state.vm_clock_seqlock); seqlock_write_lock(&timers_state.vm_clock_seqlock);
timers_state.qemu_icount_bias += warp; timers_state.qemu_icount_bias += warp;
seqlock_write_unlock(&timers_state.vm_clock_seqlock); seqlock_write_unlock(&timers_state.vm_clock_seqlock);
qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); 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); clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
} }
qemu_clock_notify(QEMU_CLOCK_VIRTUAL); qemu_clock_notify(QEMU_CLOCK_VIRTUAL);

9
hmp.c
View File

@ -16,6 +16,7 @@
#include "hmp.h" #include "hmp.h"
#include "net/net.h" #include "net/net.h"
#include "sysemu/char.h" #include "sysemu/char.h"
#include "sysemu/block-backend.h"
#include "qemu/option.h" #include "qemu/option.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "qmp-commands.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) void hmp_qemu_io(Monitor *mon, const QDict *qdict)
{ {
BlockDriverState *bs; BlockBackend *blk;
const char* device = qdict_get_str(qdict, "device"); const char* device = qdict_get_str(qdict, "device");
const char* command = qdict_get_str(qdict, "command"); const char* command = qdict_get_str(qdict, "command");
Error *err = NULL; Error *err = NULL;
bs = bdrv_find(device); blk = blk_by_name(device);
if (bs) { if (blk) {
qemuio_command(bs, command); qemuio_command(blk, command);
} else { } else {
error_set(&err, QERR_DEVICE_NOT_FOUND, device); error_set(&err, QERR_DEVICE_NOT_FOUND, device);
} }

View File

@ -16,7 +16,9 @@
#include "qemu/iov.h" #include "qemu/iov.h"
#include "qemu/thread.h" #include "qemu/thread.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "hw/virtio/virtio-access.h"
#include "hw/virtio/dataplane/vring.h" #include "hw/virtio/dataplane/vring.h"
#include "hw/virtio/dataplane/vring-accessors.h"
#include "sysemu/block-backend.h" #include "sysemu/block-backend.h"
#include "hw/virtio/virtio-blk.h" #include "hw/virtio/virtio-blk.h"
#include "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; VirtIOBlockDataPlane *s = req->dev->dataplane;
stb_p(&req->in->status, status); 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)); req->qiov.size + sizeof(*req->in));
/* Suppress notification to guest by BH and its scheduled /* Suppress notification to guest by BH and its scheduled

View File

@ -40,6 +40,8 @@
#include "xen_blkif.h" #include "xen_blkif.h"
#include "sysemu/blockdev.h" #include "sysemu/blockdev.h"
#include "sysemu/block-backend.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); blkdev->dinfo = drive_get(IF_XEN, 0, index);
if (!blkdev->dinfo) { if (!blkdev->dinfo) {
Error *local_err = NULL; Error *local_err = NULL;
BlockBackend *blk; QDict *options = NULL;
BlockDriver *drv;
BlockDriverState *bs; 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 */ /* setup via xenbus -> create new block driver instance */
xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
blk = blk_new_with_bs(blkdev->dev, NULL); blkdev->blk = blk_new_open(blkdev->dev, blkdev->filename, NULL, options,
if (!blk) { qflags, &local_err);
return -1; if (!blkdev->blk) {
}
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) {
xen_be_printf(&blkdev->xendev, 0, "error: %s\n", xen_be_printf(&blkdev->xendev, 0, "error: %s\n",
error_get_pretty(local_err)); error_get_pretty(local_err));
error_free(local_err); error_free(local_err);
blk_unref(blk);
blkdev->blk = NULL;
return -1; return -1;
} }
assert(bs == blk_bs(blk));
} else { } else {
/* setup via qemu cmdline -> already setup for us */ /* setup via qemu cmdline -> already setup for us */
xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n"); xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n");

View File

@ -94,7 +94,7 @@ void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req)
{ {
VirtIODevice *vdev = VIRTIO_DEVICE(req->vring->parent); 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); req->qsgl.size + req->resp_iov.size);
if (vring_should_notify(vdev, &req->vring->vring)) { if (vring_should_notify(vdev, &req->vring->vring)) {

View File

@ -2,7 +2,7 @@ common-obj-y += virtio-rng.o
common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
common-obj-y += virtio-bus.o common-obj-y += virtio-bus.o
common-obj-y += virtio-mmio.o common-obj-y += virtio-mmio.o
common-obj-$(CONFIG_VIRTIO) += dataplane/ obj-$(CONFIG_VIRTIO) += dataplane/
obj-y += virtio.o virtio-balloon.o obj-y += virtio.o virtio-balloon.o
obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o

View File

@ -1 +1 @@
common-obj-y += vring.o obj-y += vring.o

View File

@ -18,7 +18,9 @@
#include "hw/hw.h" #include "hw/hw.h"
#include "exec/memory.h" #include "exec/memory.h"
#include "exec/address-spaces.h" #include "exec/address-spaces.h"
#include "hw/virtio/virtio-access.h"
#include "hw/virtio/dataplane/vring.h" #include "hw/virtio/dataplane/vring.h"
#include "hw/virtio/dataplane/vring-accessors.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
/* vring_map can be coupled with vring_unmap or (if you still have the /* 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_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_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 = 0;
vring->signalled_used_valid = false; 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) void vring_disable_notification(VirtIODevice *vdev, Vring *vring)
{ {
if (!(vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX))) { 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)) { if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) {
vring_avail_event(&vring->vr) = vring->vr.avail->idx; vring_avail_event(&vring->vr) = vring->vr.avail->idx;
} else { } 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 */ 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() */ /* 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(); smp_mb();
if ((vdev->guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) && 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; return true;
} }
if (!(vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX))) { 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; old = vring->signalled_used;
v = vring->signalled_used_valid; v = vring->signalled_used_valid;
@ -202,9 +205,19 @@ static int get_desc(Vring *vring, VirtQueueElement *elem,
return 0; 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. */ /* This is stolen from linux/drivers/vhost/vhost.c. */
static int get_indirect(Vring *vring, VirtQueueElement *elem, static int get_indirect(VirtIODevice *vdev, Vring *vring,
struct vring_desc *indirect) VirtQueueElement *elem, struct vring_desc *indirect)
{ {
struct vring_desc desc; struct vring_desc desc;
unsigned int i = 0, count, found = 0; unsigned int i = 0, count, found = 0;
@ -244,7 +257,7 @@ static int get_indirect(Vring *vring, VirtQueueElement *elem,
vring->broken = true; vring->broken = true;
return -EFAULT; return -EFAULT;
} }
desc = *desc_ptr; copy_in_vring_desc(vdev, desc_ptr, &desc);
memory_region_unref(mr); memory_region_unref(mr);
/* Ensure descriptor has been loaded before accessing fields */ /* 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. */ /* Check it isn't doing very strange things with descriptor numbers. */
last_avail_idx = vring->last_avail_idx; 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 */ barrier(); /* load indices now and not again later */
if (unlikely((uint16_t)(avail_idx - last_avail_idx) > num)) { 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 /* Grab the next descriptor number they're advertising, and increment
* the index we've seen. */ * 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; elem->index = head;
@ -365,13 +378,13 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
ret = -EFAULT; ret = -EFAULT;
goto out; goto out;
} }
desc = vring->vr.desc[i]; copy_in_vring_desc(vdev, &vring->vr.desc[i], &desc);
/* Ensure descriptor is loaded before accessing fields */ /* Ensure descriptor is loaded before accessing fields */
barrier(); barrier();
if (desc.flags & VRING_DESC_F_INDIRECT) { if (desc.flags & VRING_DESC_F_INDIRECT) {
ret = get_indirect(vring, elem, &desc); ret = get_indirect(vdev, vring, elem, &desc);
if (ret < 0) { if (ret < 0) {
goto out; goto out;
} }
@ -407,9 +420,9 @@ out:
* *
* Stolen from linux/drivers/vhost/vhost.c. * 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; unsigned int head = elem->index;
uint16_t new; 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 /* The virtqueue contains a ring of used buffers. Get a pointer to the
* next entry in that used ring. */ * next entry in that used ring. */
used = &vring->vr.used->ring[vring->last_used_idx % vring->vr.num]; vring_set_used_ring_id(vdev, vring, vring->last_used_idx % vring->vr.num,
used->id = head; head);
used->len = len; vring_set_used_ring_len(vdev, vring, vring->last_used_idx % vring->vr.num,
len);
/* Make sure buffer is written before we update index. */ /* Make sure buffer is written before we update index. */
smp_wmb(); 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)) { if (unlikely((int16_t)(new - vring->signalled_used) < (uint16_t)1)) {
vring->signalled_used_valid = false; vring->signalled_used_valid = false;
} }

View File

@ -168,7 +168,8 @@ void bdrv_io_limits_disable(BlockDriverState *bs);
void bdrv_init(void); void bdrv_init(void);
void bdrv_init_with_whitelist(void); void bdrv_init_with_whitelist(void);
BlockDriver *bdrv_find_protocol(const char *filename, 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_format(const char *format_name);
BlockDriver *bdrv_find_whitelisted_format(const char *format_name, BlockDriver *bdrv_find_whitelisted_format(const char *format_name,
bool readonly); bool readonly);

View File

@ -369,9 +369,6 @@ struct BlockDriverState {
/* I/O Limits */ /* I/O Limits */
BlockLimits bl; BlockLimits bl;
/* Whether the disk can expand beyond total_sectors */
int growable;
/* Whether produces zeros when read beyond eof */ /* Whether produces zeros when read beyond eof */
bool zero_beyond_eof; bool zero_beyond_eof;

View File

@ -99,7 +99,6 @@ void nbd_export_close_all(void);
NBDClient *nbd_client_new(NBDExport *exp, int csock, NBDClient *nbd_client_new(NBDExport *exp, int csock,
void (*close)(NBDClient *)); void (*close)(NBDClient *));
void nbd_client_close(NBDClient *client);
void nbd_client_get(NBDClient *client); void nbd_client_get(NBDClient *client);
void nbd_client_put(NBDClient *client); void nbd_client_put(NBDClient *client);

View File

@ -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

View File

@ -31,17 +31,6 @@ typedef struct {
bool broken; /* was there a fatal error? */ bool broken; /* was there a fatal error? */
} Vring; } 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 */ /* Fail future vring_pop() and vring_push() calls until reset */
static inline void vring_set_broken(Vring *vring) 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_enable_notification(VirtIODevice *vdev, Vring *vring);
bool vring_should_notify(VirtIODevice *vdev, Vring *vring); bool vring_should_notify(VirtIODevice *vdev, Vring *vring);
int vring_pop(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem); 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 */ #endif /* VRING_H */

View File

@ -22,7 +22,7 @@
#define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */ #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 void (*helpfunc_t)(void);
typedef struct cmdinfo { typedef struct cmdinfo {
@ -40,7 +40,7 @@ typedef struct cmdinfo {
extern bool qemuio_misalign; 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); void qemuio_add_command(const cmdinfo_t *ci);
int qemuio_command_usage(const cmdinfo_t *ci); int qemuio_command_usage(const cmdinfo_t *ci);

View File

@ -62,6 +62,9 @@ typedef struct BlockDevOps {
BlockBackend *blk_new(const char *name, Error **errp); BlockBackend *blk_new(const char *name, Error **errp);
BlockBackend *blk_new_with_bs(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_ref(BlockBackend *blk);
void blk_unref(BlockBackend *blk); void blk_unref(BlockBackend *blk);
const char *blk_name(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); int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count);
int64_t blk_getlength(BlockBackend *blk); int64_t blk_getlength(BlockBackend *blk);
void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr); 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, BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num,
QEMUIOVector *iov, int nb_sectors, QEMUIOVector *iov, int nb_sectors,
BlockCompletionFunc *cb, void *opaque); BlockCompletionFunc *cb, void *opaque);
@ -151,5 +155,14 @@ BlockAcctStats *blk_get_stats(BlockBackend *blk);
void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk,
BlockCompletionFunc *cb, void *opaque); 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 #endif

8
nbd.c
View File

@ -874,7 +874,7 @@ void nbd_client_put(NBDClient *client)
{ {
if (--client->refcount == 0) { if (--client->refcount == 0) {
/* The last reference should be dropped by client->close, /* 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); 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) { if (client->closing) {
return; return;
@ -1026,7 +1026,7 @@ void nbd_export_close(NBDExport *exp)
nbd_export_get(exp); nbd_export_get(exp);
QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) { QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) {
nbd_client_close(client); client_close(client);
} }
nbd_export_set_name(exp, NULL); nbd_export_set_name(exp, NULL);
nbd_export_put(exp); nbd_export_put(exp);
@ -1311,7 +1311,7 @@ done:
out: out:
nbd_request_put(req); nbd_request_put(req);
nbd_client_close(client); client_close(client);
} }
static void nbd_read(void *opaque) static void nbd_read(void *opaque)

View File

@ -261,6 +261,7 @@ static int print_block_option_help(const char *filename, const char *fmt)
{ {
BlockDriver *drv, *proto_drv; BlockDriver *drv, *proto_drv;
QemuOptsList *create_opts = NULL; QemuOptsList *create_opts = NULL;
Error *local_err = NULL;
/* Find driver and parse its options */ /* Find driver and parse its options */
drv = bdrv_find_format(fmt); 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); create_opts = qemu_opts_append(create_opts, drv->create_opts);
if (filename) { if (filename) {
proto_drv = bdrv_find_protocol(filename, true); proto_drv = bdrv_find_protocol(filename, true, &local_err);
if (!proto_drv) { if (!proto_drv) {
error_report("Unknown protocol '%s'", filename); qerror_report_err(local_err);
error_free(local_err);
qemu_opts_free(create_opts); qemu_opts_free(create_opts);
return 1; return 1;
} }
@ -291,32 +293,24 @@ static BlockBackend *img_open(const char *id, const char *filename,
{ {
BlockBackend *blk; BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
BlockDriver *drv;
char password[256]; char password[256];
Error *local_err = NULL; Error *local_err = NULL;
int ret; QDict *options = NULL;
blk = blk_new_with_bs(id, &error_abort);
bs = blk_bs(blk);
if (fmt) { if (fmt) {
drv = bdrv_find_format(fmt); options = qdict_new();
if (!drv) { qdict_put(options, "driver", qstring_from_str(fmt));
error_report("Unknown file format '%s'", fmt);
goto fail;
}
} else {
drv = NULL;
} }
ret = bdrv_open(&bs, filename, NULL, NULL, flags, drv, &local_err); blk = blk_new_open(id, filename, NULL, options, flags, &local_err);
if (ret < 0) { if (!blk) {
error_report("Could not open '%s': %s", filename, error_report("Could not open '%s': %s", filename,
error_get_pretty(local_err)); error_get_pretty(local_err));
error_free(local_err); error_free(local_err);
goto fail; goto fail;
} }
bs = blk_bs(blk);
if (bdrv_is_encrypted(bs) && require_io) { if (bdrv_is_encrypted(bs) && require_io) {
qprintf(quiet, "Disk image '%s' is encrypted.\n", filename); qprintf(quiet, "Disk image '%s' is encrypted.\n", filename);
if (read_password(password, sizeof(password)) < 0) { 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 * Returns 0 in case sectors are filled with 0, 1 if sectors contain non-zero
* data and negative value on error. * 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_num: Number of first sector to check
* @param sect_count: Number of sectors to check * @param sect_count: Number of sectors to check
* @param filename: Name of disk file we are checking (logging purpose) * @param filename: Name of disk file we are checking (logging purpose)
* @param buffer: Allocated buffer for storing read data * @param buffer: Allocated buffer for storing read data
* @param quiet: Flag for quiet mode * @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, int sect_count, const char *filename,
uint8_t *buffer, bool quiet) uint8_t *buffer, bool quiet)
{ {
int pnum, ret = 0; 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) { if (ret < 0) {
error_report("Error while reading offset %" PRId64 " of %s: %s", error_report("Error while reading offset %" PRId64 " of %s: %s",
sectors_to_bytes(sect_num), filename, strerror(-ret)); sectors_to_bytes(sect_num), filename, strerror(-ret));
@ -1138,16 +1132,16 @@ static int img_compare(int argc, char **argv)
} }
bs2 = blk_bs(blk2); bs2 = blk_bs(blk2);
buf1 = qemu_blockalign(bs1, IO_BUF_SIZE); buf1 = blk_blockalign(blk1, IO_BUF_SIZE);
buf2 = qemu_blockalign(bs2, IO_BUF_SIZE); buf2 = blk_blockalign(blk2, IO_BUF_SIZE);
total_sectors1 = bdrv_nb_sectors(bs1); total_sectors1 = blk_nb_sectors(blk1);
if (total_sectors1 < 0) { if (total_sectors1 < 0) {
error_report("Can't get size of %s: %s", error_report("Can't get size of %s: %s",
filename1, strerror(-total_sectors1)); filename1, strerror(-total_sectors1));
ret = 4; ret = 4;
goto out; goto out;
} }
total_sectors2 = bdrv_nb_sectors(bs2); total_sectors2 = blk_nb_sectors(blk2);
if (total_sectors2 < 0) { if (total_sectors2 < 0) {
error_report("Can't get size of %s: %s", error_report("Can't get size of %s: %s",
filename2, strerror(-total_sectors2)); filename2, strerror(-total_sectors2));
@ -1189,7 +1183,7 @@ static int img_compare(int argc, char **argv)
if (allocated1 == allocated2) { if (allocated1 == allocated2) {
if (allocated1) { if (allocated1) {
ret = bdrv_read(bs1, sector_num, buf1, nb_sectors); ret = blk_read(blk1, sector_num, buf1, nb_sectors);
if (ret < 0) { if (ret < 0) {
error_report("Error while reading offset %" PRId64 " of %s:" error_report("Error while reading offset %" PRId64 " of %s:"
" %s", sectors_to_bytes(sector_num), filename1, " %s", sectors_to_bytes(sector_num), filename1,
@ -1197,7 +1191,7 @@ static int img_compare(int argc, char **argv)
ret = 4; ret = 4;
goto out; goto out;
} }
ret = bdrv_read(bs2, sector_num, buf2, nb_sectors); ret = blk_read(blk2, sector_num, buf2, nb_sectors);
if (ret < 0) { if (ret < 0) {
error_report("Error while reading offset %" PRId64 error_report("Error while reading offset %" PRId64
" of %s: %s", sectors_to_bytes(sector_num), " of %s: %s", sectors_to_bytes(sector_num),
@ -1224,10 +1218,10 @@ static int img_compare(int argc, char **argv)
} }
if (allocated1) { if (allocated1) {
ret = check_empty_sectors(bs1, sector_num, nb_sectors, ret = check_empty_sectors(blk1, sector_num, nb_sectors,
filename1, buf1, quiet); filename1, buf1, quiet);
} else { } else {
ret = check_empty_sectors(bs2, sector_num, nb_sectors, ret = check_empty_sectors(blk2, sector_num, nb_sectors,
filename2, buf1, quiet); filename2, buf1, quiet);
} }
if (ret) { if (ret) {
@ -1244,18 +1238,18 @@ static int img_compare(int argc, char **argv)
} }
if (total_sectors1 != total_sectors2) { if (total_sectors1 != total_sectors2) {
BlockDriverState *bs_over; BlockBackend *blk_over;
int64_t total_sectors_over; int64_t total_sectors_over;
const char *filename_over; const char *filename_over;
qprintf(quiet, "Warning: Image size mismatch!\n"); qprintf(quiet, "Warning: Image size mismatch!\n");
if (total_sectors1 > total_sectors2) { if (total_sectors1 > total_sectors2) {
total_sectors_over = total_sectors1; total_sectors_over = total_sectors1;
bs_over = bs1; blk_over = blk1;
filename_over = filename1; filename_over = filename1;
} else { } else {
total_sectors_over = total_sectors2; total_sectors_over = total_sectors2;
bs_over = bs2; blk_over = blk2;
filename_over = filename2; filename_over = filename2;
} }
@ -1264,7 +1258,7 @@ static int img_compare(int argc, char **argv)
if (nb_sectors <= 0) { if (nb_sectors <= 0) {
break; 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); nb_sectors, &pnum);
if (ret < 0) { if (ret < 0) {
ret = 3; ret = 3;
@ -1275,7 +1269,7 @@ static int img_compare(int argc, char **argv)
} }
nb_sectors = pnum; nb_sectors = pnum;
if (ret) { 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); filename_over, buf1, quiet);
if (ret) { if (ret) {
if (ret < 0) { if (ret < 0) {
@ -1484,7 +1478,7 @@ static int img_convert(int argc, char **argv)
goto out; goto out;
} }
bs[bs_i] = blk_bs(blk[bs_i]); 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) { if (bs_sectors[bs_i] < 0) {
error_report("Could not get size of %s: %s", error_report("Could not get size of %s: %s",
argv[optind + bs_i], strerror(-bs_sectors[bs_i])); argv[optind + bs_i], strerror(-bs_sectors[bs_i]));
@ -1524,41 +1518,44 @@ static int img_convert(int argc, char **argv)
goto out; goto out;
} }
proto_drv = bdrv_find_protocol(out_filename, true); proto_drv = bdrv_find_protocol(out_filename, true, &local_err);
if (!proto_drv) { if (!proto_drv) {
error_report("Unknown protocol '%s'", out_filename); qerror_report_err(local_err);
error_free(local_err);
ret = -1; ret = -1;
goto out; goto out;
} }
if (!drv->create_opts) { if (!skip_create) {
error_report("Format driver '%s' does not support image creation", if (!drv->create_opts) {
drv->format_name); error_report("Format driver '%s' does not support image creation",
ret = -1; drv->format_name);
goto out; ret = -1;
} goto out;
}
if (!proto_drv->create_opts) { if (!proto_drv->create_opts) {
error_report("Protocol driver '%s' does not support image creation", error_report("Protocol driver '%s' does not support image creation",
proto_drv->format_name); proto_drv->format_name);
ret = -1; ret = -1;
goto out; goto out;
} }
create_opts = qemu_opts_append(create_opts, drv->create_opts); 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, proto_drv->create_opts);
opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
if (options && qemu_opts_do_parse(opts, options, NULL)) { if (options && qemu_opts_do_parse(opts, options, NULL)) {
error_report("Invalid options for file format '%s'", out_fmt); error_report("Invalid options for file format '%s'", out_fmt);
ret = -1; ret = -1;
goto out; goto out;
} }
qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_sectors * 512); qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_sectors * 512);
ret = add_old_style_options(out_fmt, opts, out_baseimg, NULL); ret = add_old_style_options(out_fmt, opts, out_baseimg, NULL);
if (ret < 0) { if (ret < 0) {
goto out; goto out;
}
} }
/* Get backing file name if -o backing_file was used */ /* 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)) 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) { 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) { if (output_sectors < 0) {
error_report("unable to get output image length: %s\n", error_report("unable to get output image length: %s\n",
strerror(-output_sectors)); strerror(-output_sectors));
@ -1704,7 +1701,7 @@ static int img_convert(int argc, char **argv)
nlow = remainder > bs_sectors[bs_i] - bs_num nlow = remainder > bs_sectors[bs_i] - bs_num
? bs_sectors[bs_i] - bs_num : remainder; ? 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) { if (ret < 0) {
error_report("error while reading sector %" PRId64 ": %s", error_report("error while reading sector %" PRId64 ": %s",
bs_num, strerror(-ret)); bs_num, strerror(-ret));
@ -1719,7 +1716,7 @@ static int img_convert(int argc, char **argv)
assert (remainder == 0); assert (remainder == 0);
if (!buffer_is_zero(buf, n * BDRV_SECTOR_SIZE)) { 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) { if (ret != 0) {
error_report("error while compressing sector %" PRId64 error_report("error while compressing sector %" PRId64
": %s", sector_num, strerror(-ret)); ": %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); qemu_progress_print(100.0 * sector_num / total_sectors, 0);
} }
/* signal EOF to align */ /* signal EOF to align */
bdrv_write_compressed(out_bs, 0, NULL, 0); blk_write_compressed(out_blk, 0, NULL, 0);
} else { } else {
int64_t sectors_to_read, sectors_read, sector_num_next_status; int64_t sectors_to_read, sectors_read, sector_num_next_status;
bool count_allocated_sectors; bool count_allocated_sectors;
@ -1831,7 +1828,7 @@ restart:
} }
n1 = n; 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) { if (ret < 0) {
error_report("error while reading sector %" PRId64 ": %s", error_report("error while reading sector %" PRId64 ": %s",
sector_num - bs_offset, strerror(-ret)); sector_num - bs_offset, strerror(-ret));
@ -1844,7 +1841,7 @@ restart:
while (n > 0) { while (n > 0) {
if (!has_zero_init || if (!has_zero_init ||
is_allocated_sectors_min(buf1, n, &n1, min_sparse)) { 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) { if (ret < 0) {
error_report("error while writing sector %" PRId64 error_report("error while writing sector %" PRId64
": %s", sector_num, strerror(-ret)); ": %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"); 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) { while (curr.start + curr.length < length) {
int64_t nsectors_left; int64_t nsectors_left;
int64_t sector_num; int64_t sector_num;
@ -2437,8 +2434,7 @@ static int img_snapshot(int argc, char **argv)
static int img_rebase(int argc, char **argv) static int img_rebase(int argc, char **argv)
{ {
BlockBackend *blk = NULL, *blk_old_backing = NULL, *blk_new_backing = NULL; BlockBackend *blk = NULL, *blk_old_backing = NULL, *blk_new_backing = NULL;
BlockDriverState *bs = NULL, *bs_old_backing = NULL, *bs_new_backing = NULL; BlockDriverState *bs = NULL;
BlockDriver *old_backing_drv, *new_backing_drv;
char *filename; char *filename;
const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg; const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg;
int c, flags, src_flags, ret; int c, flags, src_flags, ret;
@ -2532,22 +2528,8 @@ static int img_rebase(int argc, char **argv)
} }
bs = blk_bs(blk); 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) { if (out_basefmt != NULL) {
new_backing_drv = bdrv_find_format(out_basefmt); if (bdrv_find_format(out_basefmt) == NULL) {
if (new_backing_drv == NULL) {
error_report("Invalid format name: '%s'", out_basefmt); error_report("Invalid format name: '%s'", out_basefmt);
ret = -1; ret = -1;
goto out; 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 */ /* For safe rebasing we need to compare old and new backing file */
if (!unsafe) { if (!unsafe) {
char backing_name[PATH_MAX]; 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)); bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name));
ret = bdrv_open(&bs_old_backing, backing_name, NULL, NULL, src_flags, blk_old_backing = blk_new_open("old_backing", backing_name, NULL,
old_backing_drv, &local_err); options, src_flags, &local_err);
if (ret) { if (!blk_old_backing) {
error_report("Could not open old backing file '%s': %s", error_report("Could not open old backing file '%s': %s",
backing_name, error_get_pretty(local_err)); backing_name, error_get_pretty(local_err));
error_free(local_err); error_free(local_err);
goto out; goto out;
} }
if (out_baseimg[0]) { if (out_baseimg[0]) {
blk_new_backing = blk_new_with_bs("new_backing", &error_abort); if (out_basefmt) {
bs_new_backing = blk_bs(blk_new_backing); options = qdict_new();
ret = bdrv_open(&bs_new_backing, out_baseimg, NULL, NULL, src_flags, qdict_put(options, "driver", qstring_from_str(out_basefmt));
new_backing_drv, &local_err); } else {
if (ret) { 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", error_report("Could not open new backing file '%s': %s",
out_baseimg, error_get_pretty(local_err)); out_baseimg, error_get_pretty(local_err));
error_free(local_err); error_free(local_err);
@ -2602,17 +2594,17 @@ static int img_rebase(int argc, char **argv)
uint8_t * buf_new; uint8_t * buf_new;
float local_progress = 0; float local_progress = 0;
buf_old = qemu_blockalign(bs, IO_BUF_SIZE); buf_old = blk_blockalign(blk, IO_BUF_SIZE);
buf_new = qemu_blockalign(bs, 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) { if (num_sectors < 0) {
error_report("Could not get size of '%s': %s", error_report("Could not get size of '%s': %s",
filename, strerror(-num_sectors)); filename, strerror(-num_sectors));
ret = -1; ret = -1;
goto out; 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) { if (old_backing_num_sectors < 0) {
char backing_name[PATH_MAX]; char backing_name[PATH_MAX];
@ -2622,8 +2614,8 @@ static int img_rebase(int argc, char **argv)
ret = -1; ret = -1;
goto out; goto out;
} }
if (bs_new_backing) { if (blk_new_backing) {
new_backing_num_sectors = bdrv_nb_sectors(bs_new_backing); new_backing_num_sectors = blk_nb_sectors(blk_new_backing);
if (new_backing_num_sectors < 0) { if (new_backing_num_sectors < 0) {
error_report("Could not get size of '%s': %s", error_report("Could not get size of '%s': %s",
out_baseimg, strerror(-new_backing_num_sectors)); 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; 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) { if (ret < 0) {
error_report("error while reading from old backing file"); error_report("error while reading from old backing file");
goto out; 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); memset(buf_new, 0, n * BDRV_SECTOR_SIZE);
} else { } else {
if (sector + n > new_backing_num_sectors) { if (sector + n > new_backing_num_sectors) {
n = new_backing_num_sectors - sector; 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) { if (ret < 0) {
error_report("error while reading from new backing file"); error_report("error while reading from new backing file");
goto out; goto out;
@ -2698,8 +2690,8 @@ static int img_rebase(int argc, char **argv)
if (compare_sectors(buf_old + written * 512, if (compare_sectors(buf_old + written * 512,
buf_new + written * 512, n - written, &pnum)) buf_new + written * 512, n - written, &pnum))
{ {
ret = bdrv_write(bs, sector + written, ret = blk_write(blk, sector + written,
buf_old + written * 512, pnum); buf_old + written * 512, pnum);
if (ret < 0) { if (ret < 0) {
error_report("Error while writing to COW image: %s", error_report("Error while writing to COW image: %s",
strerror(-ret)); strerror(-ret));
@ -2764,7 +2756,6 @@ static int img_resize(int argc, char **argv)
int64_t n, total_size; int64_t n, total_size;
bool quiet = false; bool quiet = false;
BlockBackend *blk = NULL; BlockBackend *blk = NULL;
BlockDriverState *bs = NULL;
QemuOpts *param; QemuOpts *param;
static QemuOptsList resize_options = { static QemuOptsList resize_options = {
.name = "resize_options", .name = "resize_options",
@ -2846,10 +2837,9 @@ static int img_resize(int argc, char **argv)
ret = -1; ret = -1;
goto out; goto out;
} }
bs = blk_bs(blk);
if (relative) { if (relative) {
total_size = bdrv_getlength(bs) + n * relative; total_size = blk_getlength(blk) + n * relative;
} else { } else {
total_size = n; total_size = n;
} }
@ -2859,7 +2849,7 @@ static int img_resize(int argc, char **argv)
goto out; goto out;
} }
ret = bdrv_truncate(bs, total_size); ret = blk_truncate(blk, total_size);
switch (ret) { switch (ret) {
case 0: case 0:
qprintf(quiet, "Image resized.\n"); qprintf(quiet, "Image resized.\n");

View File

@ -9,10 +9,13 @@
*/ */
#include "qemu-io.h" #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 "block/qapi.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "sysemu/block-backend.h"
#define CMD_NOFILE_OK 0x01 #define CMD_NOFILE_OK 0x01
@ -40,24 +43,24 @@ int qemuio_command_usage(const cmdinfo_t *ci)
return 0; 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) { if (ct->flags & CMD_FLAG_GLOBAL) {
return 1; 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"); fprintf(stderr, "no file open, try 'help open'\n");
return 0; return 0;
} }
return 1; 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 **argv)
{ {
char *cmd = argv[0]; char *cmd = argv[0];
if (!init_check_command(bs, ct)) { if (!init_check_command(blk, ct)) {
return 0; return 0;
} }
@ -78,7 +81,7 @@ static int command(BlockDriverState *bs, const cmdinfo_t *ct, int argc,
return 0; return 0;
} }
optind = 0; optind = 0;
return ct->cfunc(bs, argc, argv); return ct->cfunc(blk, argc, argv);
} }
static const cmdinfo_t *find_command(const char *cmd) static const cmdinfo_t *find_command(const char *cmd)
@ -267,14 +270,14 @@ static int parse_pattern(const char *arg)
*/ */
#define MISALIGN_OFFSET 16 #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; void *buf;
if (qemuio_misalign) { if (qemuio_misalign) {
len += MISALIGN_OFFSET; len += MISALIGN_OFFSET;
} }
buf = qemu_blockalign(bs, len); buf = blk_blockalign(blk, len);
memset(buf, pattern, len); memset(buf, pattern, len);
if (qemuio_misalign) { if (qemuio_misalign) {
buf += MISALIGN_OFFSET; buf += MISALIGN_OFFSET;
@ -340,7 +343,7 @@ static void print_report(const char *op, struct timeval *t, int64_t offset,
* vector matching it. * vector matching it.
*/ */
static void * 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) int pattern)
{ {
size_t *sizes = g_new0(size_t, nr_iov); 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); 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++) { for (i = 0; i < nr_iov; i++) {
qemu_iovec_add(qiov, p, sizes[i]); qemu_iovec_add(qiov, p, sizes[i]);
@ -389,12 +392,12 @@ fail:
return buf; 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 *total)
{ {
int ret; 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) { if (ret < 0) {
return ret; return ret;
} }
@ -402,12 +405,12 @@ static int do_read(BlockDriverState *bs, char *buf, int64_t offset, int count,
return 1; 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 *total)
{ {
int ret; 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) { if (ret < 0) {
return ret; return ret;
} }
@ -415,20 +418,20 @@ static int do_write(BlockDriverState *bs, char *buf, int64_t offset, int count,
return 1; 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) int *total)
{ {
*total = bdrv_pread(bs, offset, (uint8_t *)buf, count); *total = blk_pread(blk, offset, (uint8_t *)buf, count);
if (*total < 0) { if (*total < 0) {
return *total; return *total;
} }
return 1; 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) int *total)
{ {
*total = bdrv_pwrite(bs, offset, (uint8_t *)buf, count); *total = blk_pwrite(blk, offset, (uint8_t *)buf, count);
if (*total < 0) { if (*total < 0) {
return *total; return *total;
} }
@ -436,7 +439,7 @@ static int do_pwrite(BlockDriverState *bs, char *buf, int64_t offset, int count,
} }
typedef struct { typedef struct {
BlockDriverState *bs; BlockBackend *blk;
int64_t offset; int64_t offset;
int count; int count;
int *total; int *total;
@ -448,8 +451,8 @@ static void coroutine_fn co_write_zeroes_entry(void *opaque)
{ {
CoWriteZeroes *data = opaque; CoWriteZeroes *data = opaque;
data->ret = bdrv_co_write_zeroes(data->bs, data->offset / BDRV_SECTOR_SIZE, data->ret = blk_co_write_zeroes(data->blk, data->offset / BDRV_SECTOR_SIZE,
data->count / BDRV_SECTOR_SIZE, 0); data->count / BDRV_SECTOR_SIZE, 0);
data->done = true; data->done = true;
if (data->ret < 0) { if (data->ret < 0) {
*data->total = data->ret; *data->total = data->ret;
@ -459,12 +462,12 @@ static void coroutine_fn co_write_zeroes_entry(void *opaque)
*data->total = data->count; *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) int *total)
{ {
Coroutine *co; Coroutine *co;
CoWriteZeroes data = { CoWriteZeroes data = {
.bs = bs, .blk = blk,
.offset = offset, .offset = offset,
.count = count, .count = count,
.total = total, .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); co = qemu_coroutine_create(co_write_zeroes_entry);
qemu_coroutine_enter(co, &data); qemu_coroutine_enter(co, &data);
while (!data.done) { while (!data.done) {
aio_poll(bdrv_get_aio_context(bs), true); aio_poll(blk_get_aio_context(blk), true);
} }
if (data.ret < 0) { if (data.ret < 0) {
return data.ret; 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 count, int *total)
{ {
int ret; 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) { if (ret < 0) {
return ret; return ret;
} }
@ -496,20 +499,20 @@ static int do_write_compressed(BlockDriverState *bs, char *buf, int64_t offset,
return 1; 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) 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) { if (*total < 0) {
return *total; return *total;
} }
return 1; 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) 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) { if (*total < 0) {
return *total; return *total;
} }
@ -522,13 +525,13 @@ static void aio_rw_done(void *opaque, int ret)
*(int *)opaque = 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) int64_t offset, int *total)
{ {
int async_ret = NOT_DONE; int async_ret = NOT_DONE;
bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9, blk_aio_readv(blk, offset >> 9, qiov, qiov->size >> 9,
aio_rw_done, &async_ret); aio_rw_done, &async_ret);
while (async_ret == NOT_DONE) { while (async_ret == NOT_DONE) {
main_loop_wait(false); main_loop_wait(false);
} }
@ -537,13 +540,13 @@ static int do_aio_readv(BlockDriverState *bs, QEMUIOVector *qiov,
return async_ret < 0 ? async_ret : 1; 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) int64_t offset, int *total)
{ {
int async_ret = NOT_DONE; int async_ret = NOT_DONE;
bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9, blk_aio_writev(blk, offset >> 9, qiov, qiov->size >> 9,
aio_rw_done, &async_ret); aio_rw_done, &async_ret);
while (async_ret == NOT_DONE) { while (async_ret == NOT_DONE) {
main_loop_wait(false); 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 num_reqs, int *total)
{ {
int i, ret; int i, ret;
@ -583,7 +586,7 @@ static int do_aio_multiwrite(BlockDriverState *bs, BlockRequest* reqs,
*total += reqs[i].qiov->size; *total += reqs[i].qiov->size;
} }
ret = bdrv_aio_multiwrite(bs, reqs, num_reqs); ret = blk_aio_multiwrite(blk, reqs, num_reqs);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -609,7 +612,7 @@ static void read_help(void)
" -b, -- read from the VM state rather than the virtual disk\n" " -b, -- read from the VM state rather than the virtual disk\n"
" -C, -- report statistics in a machine parsable format\n" " -C, -- report statistics in a machine parsable format\n"
" -l, -- length for pattern verification (only with -P)\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" " -P, -- use a pattern to verify read data\n"
" -q, -- quiet mode, do not show I/O statistics\n" " -q, -- quiet mode, do not show I/O statistics\n"
" -s, -- start offset for pattern verification (only with -P)\n" " -s, -- start offset for pattern verification (only with -P)\n"
@ -617,7 +620,7 @@ static void read_help(void)
"\n"); "\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 = { static const cmdinfo_t read_cmd = {
.name = "read", .name = "read",
@ -630,7 +633,7 @@ static const cmdinfo_t read_cmd = {
.help = read_help, .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; struct timeval t1, t2;
int Cflag = 0, pflag = 0, qflag = 0, vflag = 0; 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); gettimeofday(&t1, NULL);
if (pflag) { if (pflag) {
cnt = do_pread(bs, buf, offset, count, &total); cnt = do_pread(blk, buf, offset, count, &total);
} else if (bflag) { } else if (bflag) {
cnt = do_load_vmstate(bs, buf, offset, count, &total); cnt = do_load_vmstate(blk, buf, offset, count, &total);
} else { } else {
cnt = do_read(bs, buf, offset, count, &total); cnt = do_read(blk, buf, offset, count, &total);
} }
gettimeofday(&t2, NULL); gettimeofday(&t2, NULL);
@ -801,7 +804,7 @@ static void readv_help(void)
"\n"); "\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 = { static const cmdinfo_t readv_cmd = {
.name = "readv", .name = "readv",
@ -813,7 +816,7 @@ static const cmdinfo_t readv_cmd = {
.help = readv_help, .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; struct timeval t1, t2;
int Cflag = 0, qflag = 0, vflag = 0; 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; 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) { if (buf == NULL) {
return 0; return 0;
} }
gettimeofday(&t1, NULL); gettimeofday(&t1, NULL);
cnt = do_aio_readv(bs, &qiov, offset, &total); cnt = do_aio_readv(blk, &qiov, offset, &total);
gettimeofday(&t2, NULL); gettimeofday(&t2, NULL);
if (cnt < 0) { 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" " Writes into a segment of the currently open file, using a buffer\n"
" filled with a set pattern (0xcdcdcdcd).\n" " filled with a set pattern (0xcdcdcdcd).\n"
" -b, -- write to the VM state rather than the virtual disk\n" " -b, -- write to the VM state rather than the virtual disk\n"
" -c, -- write compressed data with bdrv_write_compressed\n" " -c, -- write compressed data with blk_write_compressed\n"
" -p, -- use bdrv_pwrite to write the file\n" " -p, -- use blk_pwrite to write the file\n"
" -P, -- use different pattern to fill file\n" " -P, -- use different pattern to fill file\n"
" -C, -- report statistics in a machine parsable format\n" " -C, -- report statistics in a machine parsable format\n"
" -q, -- quiet mode, do not show I/O statistics\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"); "\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 = { static const cmdinfo_t write_cmd = {
.name = "write", .name = "write",
@ -945,7 +948,7 @@ static const cmdinfo_t write_cmd = {
.help = write_help, .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; struct timeval t1, t2;
int Cflag = 0, pflag = 0, qflag = 0, bflag = 0, Pflag = 0, zflag = 0; 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) { if (!zflag) {
buf = qemu_io_alloc(bs, count, pattern); buf = qemu_io_alloc(blk, count, pattern);
} }
gettimeofday(&t1, NULL); gettimeofday(&t1, NULL);
if (pflag) { if (pflag) {
cnt = do_pwrite(bs, buf, offset, count, &total); cnt = do_pwrite(blk, buf, offset, count, &total);
} else if (bflag) { } else if (bflag) {
cnt = do_save_vmstate(bs, buf, offset, count, &total); cnt = do_save_vmstate(blk, buf, offset, count, &total);
} else if (zflag) { } else if (zflag) {
cnt = do_co_write_zeroes(bs, offset, count, &total); cnt = do_co_write_zeroes(blk, offset, count, &total);
} else if (cflag) { } else if (cflag) {
cnt = do_write_compressed(bs, buf, offset, count, &total); cnt = do_write_compressed(blk, buf, offset, count, &total);
} else { } else {
cnt = do_write(bs, buf, offset, count, &total); cnt = do_write(blk, buf, offset, count, &total);
} }
gettimeofday(&t2, NULL); gettimeofday(&t2, NULL);
@ -1088,7 +1091,7 @@ writev_help(void)
"\n"); "\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 = { static const cmdinfo_t writev_cmd = {
.name = "writev", .name = "writev",
@ -1100,7 +1103,7 @@ static const cmdinfo_t writev_cmd = {
.help = writev_help, .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; struct timeval t1, t2;
int Cflag = 0, qflag = 0; int Cflag = 0, qflag = 0;
@ -1150,13 +1153,13 @@ static int writev_f(BlockDriverState *bs, int argc, char **argv)
} }
nr_iov = argc - optind; 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) { if (buf == NULL) {
return 0; return 0;
} }
gettimeofday(&t1, NULL); gettimeofday(&t1, NULL);
cnt = do_aio_writev(bs, &qiov, offset, &total); cnt = do_aio_writev(blk, &qiov, offset, &total);
gettimeofday(&t2, NULL); gettimeofday(&t2, NULL);
if (cnt < 0) { if (cnt < 0) {
@ -1197,7 +1200,7 @@ static void multiwrite_help(void)
"\n"); "\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 = { static const cmdinfo_t multiwrite_cmd = {
.name = "multiwrite", .name = "multiwrite",
@ -1209,7 +1212,7 @@ static const cmdinfo_t multiwrite_cmd = {
.help = multiwrite_help, .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; struct timeval t1, t2;
int Cflag = 0, qflag = 0; int Cflag = 0, qflag = 0;
@ -1290,7 +1293,7 @@ static int multiwrite_f(BlockDriverState *bs, int argc, char **argv)
nr_iov = j - optind; nr_iov = j - optind;
/* Build request */ /* 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) { if (buf[i] == NULL) {
goto out; goto out;
} }
@ -1308,7 +1311,7 @@ static int multiwrite_f(BlockDriverState *bs, int argc, char **argv)
nr_reqs = i; nr_reqs = i;
gettimeofday(&t1, NULL); gettimeofday(&t1, NULL);
cnt = do_aio_multiwrite(bs, reqs, nr_reqs, &total); cnt = do_aio_multiwrite(blk, reqs, nr_reqs, &total);
gettimeofday(&t2, NULL); gettimeofday(&t2, NULL);
if (cnt < 0) { if (cnt < 0) {
@ -1337,6 +1340,7 @@ out:
} }
struct aio_ctx { struct aio_ctx {
BlockBackend *blk;
QEMUIOVector qiov; QEMUIOVector qiov;
int64_t offset; int64_t offset;
char *buf; char *buf;
@ -1344,6 +1348,7 @@ struct aio_ctx {
int vflag; int vflag;
int Cflag; int Cflag;
int Pflag; int Pflag;
BlockAcctCookie acct;
int pattern; int pattern;
struct timeval t1; struct timeval t1;
}; };
@ -1361,6 +1366,8 @@ static void aio_write_done(void *opaque, int ret)
goto out; goto out;
} }
block_acct_done(blk_get_stats(ctx->blk), &ctx->acct);
if (ctx->qflag) { if (ctx->qflag) {
goto out; goto out;
} }
@ -1398,6 +1405,8 @@ static void aio_read_done(void *opaque, int ret)
g_free(cmp_buf); g_free(cmp_buf);
} }
block_acct_done(blk_get_stats(ctx->blk), &ctx->acct);
if (ctx->qflag) { if (ctx->qflag) {
goto out; goto out;
} }
@ -1436,7 +1445,7 @@ static void aio_read_help(void)
"\n"); "\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 = { static const cmdinfo_t aio_read_cmd = {
.name = "aio_read", .name = "aio_read",
@ -1448,11 +1457,12 @@ static const cmdinfo_t aio_read_cmd = {
.help = aio_read_help, .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; int nr_iov, c;
struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); struct aio_ctx *ctx = g_new0(struct aio_ctx, 1);
ctx->blk = blk;
while ((c = getopt(argc, argv, "CP:qv")) != EOF) { while ((c = getopt(argc, argv, "CP:qv")) != EOF) {
switch (c) { switch (c) {
case 'C': case 'C':
@ -1499,15 +1509,17 @@ static int aio_read_f(BlockDriverState *bs, int argc, char **argv)
} }
nr_iov = argc - optind; 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) { if (ctx->buf == NULL) {
g_free(ctx); g_free(ctx);
return 0; return 0;
} }
gettimeofday(&ctx->t1, NULL); gettimeofday(&ctx->t1, NULL);
bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov, block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size,
ctx->qiov.size >> 9, aio_read_done, ctx); BLOCK_ACCT_READ);
blk_aio_readv(blk, ctx->offset >> 9, &ctx->qiov,
ctx->qiov.size >> 9, aio_read_done, ctx);
return 0; return 0;
} }
@ -1531,7 +1543,7 @@ static void aio_write_help(void)
"\n"); "\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 = { static const cmdinfo_t aio_write_cmd = {
.name = "aio_write", .name = "aio_write",
@ -1543,12 +1555,13 @@ static const cmdinfo_t aio_write_cmd = {
.help = aio_write_help, .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 nr_iov, c;
int pattern = 0xcd; int pattern = 0xcd;
struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); struct aio_ctx *ctx = g_new0(struct aio_ctx, 1);
ctx->blk = blk;
while ((c = getopt(argc, argv, "CqP:")) != EOF) { while ((c = getopt(argc, argv, "CqP:")) != EOF) {
switch (c) { switch (c) {
case 'C': case 'C':
@ -1591,21 +1604,23 @@ static int aio_write_f(BlockDriverState *bs, int argc, char **argv)
} }
nr_iov = argc - optind; 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) { if (ctx->buf == NULL) {
g_free(ctx); g_free(ctx);
return 0; return 0;
} }
gettimeofday(&ctx->t1, NULL); gettimeofday(&ctx->t1, NULL);
bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov, block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size,
ctx->qiov.size >> 9, aio_write_done, ctx); BLOCK_ACCT_WRITE);
blk_aio_writev(blk, ctx->offset >> 9, &ctx->qiov,
ctx->qiov.size >> 9, aio_write_done, ctx);
return 0; 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; return 0;
} }
@ -1615,9 +1630,9 @@ static const cmdinfo_t aio_flush_cmd = {
.oneline = "completes all outstanding aio requests" .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; return 0;
} }
@ -1628,7 +1643,7 @@ static const cmdinfo_t flush_cmd = {
.oneline = "flush all in-core file state to disk", .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; int64_t offset;
int ret; int ret;
@ -1639,7 +1654,7 @@ static int truncate_f(BlockDriverState *bs, int argc, char **argv)
return 0; return 0;
} }
ret = bdrv_truncate(bs, offset); ret = blk_truncate(blk, offset);
if (ret < 0) { if (ret < 0) {
printf("truncate: %s\n", strerror(-ret)); printf("truncate: %s\n", strerror(-ret));
return 0; return 0;
@ -1658,12 +1673,12 @@ static const cmdinfo_t truncate_cmd = {
.oneline = "truncates the current file at the given offset", .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; int64_t size;
char s1[64]; char s1[64];
size = bdrv_getlength(bs); size = blk_getlength(blk);
if (size < 0) { if (size < 0) {
printf("getlength: %s\n", strerror(-size)); printf("getlength: %s\n", strerror(-size));
return 0; 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; BlockDriverInfo bdi;
ImageInfoSpecific *spec_info; ImageInfoSpecific *spec_info;
char s1[64], s2[64]; char s1[64], s2[64];
@ -1742,7 +1758,7 @@ static void discard_help(void)
"\n"); "\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 = { static const cmdinfo_t discard_cmd = {
.name = "discard", .name = "discard",
@ -1755,7 +1771,7 @@ static const cmdinfo_t discard_cmd = {
.help = discard_help, .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; struct timeval t1, t2;
int Cflag = 0, qflag = 0; int Cflag = 0, qflag = 0;
@ -1794,8 +1810,8 @@ static int discard_f(BlockDriverState *bs, int argc, char **argv)
} }
gettimeofday(&t1, NULL); gettimeofday(&t1, NULL);
ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS, ret = blk_discard(blk, offset >> BDRV_SECTOR_BITS,
count >> BDRV_SECTOR_BITS); count >> BDRV_SECTOR_BITS);
gettimeofday(&t2, NULL); gettimeofday(&t2, NULL);
if (ret < 0) { if (ret < 0) {
@ -1813,8 +1829,9 @@ out:
return 0; 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; int64_t offset, sector_num;
int nb_sectors, remaining; int nb_sectors, remaining;
char s1[64]; char s1[64];
@ -1910,20 +1927,27 @@ static int map_is_allocated(BlockDriverState *bs, int64_t sector_num,
return firstret; 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 offset;
int64_t nb_sectors; int64_t nb_sectors, total_sectors;
char s1[64]; char s1[64];
int64_t num; int64_t num;
int ret; int ret;
const char *retstr; const char *retstr;
offset = 0; 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 { do {
ret = map_is_allocated(bs, offset, nb_sectors, &num); ret = map_is_allocated(blk_bs(blk), offset, nb_sectors, &num);
if (ret < 0) { if (ret < 0) {
error_report("Failed to get allocation status: %s", strerror(-ret)); error_report("Failed to get allocation status: %s", strerror(-ret));
return 0; return 0;
@ -1940,7 +1964,7 @@ static int map_f(BlockDriverState *bs, int argc, char **argv)
offset += num; offset += num;
nb_sectors -= num; nb_sectors -= num;
} while (offset < bs->total_sectors); } while (offset < total_sectors);
return 0; return 0;
} }
@ -1954,11 +1978,11 @@ static const cmdinfo_t map_cmd = {
.oneline = "prints the allocated areas of a file", .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; 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) { if (ret < 0) {
printf("Could not set breakpoint: %s\n", strerror(-ret)); 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; 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; int ret;
ret = bdrv_debug_remove_breakpoint(bs, argv[1]); ret = bdrv_debug_remove_breakpoint(blk_bs(blk), argv[1]);
if (ret < 0) { if (ret < 0) {
printf("Could not remove breakpoint %s: %s\n", argv[1], strerror(-ret)); 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", .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; int ret;
ret = bdrv_debug_resume(bs, argv[1]); ret = bdrv_debug_resume(blk_bs(blk), argv[1]);
if (ret < 0) { if (ret < 0) {
printf("Could not resume request: %s\n", strerror(-ret)); 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", .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])) { while (!bdrv_debug_is_suspended(blk_bs(blk), argv[1])) {
aio_poll(bdrv_get_aio_context(bs), true); aio_poll(blk_get_aio_context(blk), true);
} }
return 0; return 0;
@ -2036,7 +2060,7 @@ static const cmdinfo_t wait_break_cmd = {
.oneline = "waits for the suspension of a request", .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(); abort();
} }
@ -2062,7 +2086,7 @@ static void sigraise_help(void)
"\n", SIGTERM); "\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 = { static const cmdinfo_t sigraise_cmd = {
.name = "sigraise", .name = "sigraise",
@ -2075,7 +2099,7 @@ static const cmdinfo_t sigraise_cmd = {
.help = sigraise_help, .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]); int sig = cvtnum(argv[1]);
if (sig < 0) { if (sig < 0) {
@ -2099,7 +2123,7 @@ static void sleep_cb(void *opaque)
*expired = true; *expired = true;
} }
static int sleep_f(BlockDriverState *bs, int argc, char **argv) static int sleep_f(BlockBackend *blk, int argc, char **argv)
{ {
char *endptr; char *endptr;
long ms; long ms;
@ -2168,7 +2192,7 @@ static void help_all(void)
printf("\nUse 'help commandname' for extended help.\n"); 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; const cmdinfo_t *ct;
@ -2198,7 +2222,7 @@ static const cmdinfo_t help_cmd = {
.oneline = "help for one or all commands", .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; char *input;
const cmdinfo_t *ct; const cmdinfo_t *ct;
@ -2211,7 +2235,7 @@ bool qemuio_command(BlockDriverState *bs, const char *cmd)
if (c) { if (c) {
ct = find_command(v[0]); ct = find_command(v[0]);
if (ct) { if (ct) {
done = command(bs, ct, c, v); done = command(blk, ct, c, v);
} else { } else {
fprintf(stderr, "command \"%s\" not found\n", v[0]); fprintf(stderr, "command \"%s\" not found\n", v[0]);
} }

View File

@ -28,7 +28,6 @@
static char *progname; static char *progname;
static BlockBackend *qemuio_blk; static BlockBackend *qemuio_blk;
static BlockDriverState *qemuio_bs;
/* qemu-io commands passed using -c */ /* qemu-io commands passed using -c */
static int ncmdline; static int ncmdline;
@ -36,10 +35,9 @@ static char **cmdline;
static ReadLineState *readline_state; 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); blk_unref(qemuio_blk);
qemuio_bs = NULL;
qemuio_blk = NULL; qemuio_blk = NULL;
return 0; return 0;
} }
@ -51,32 +49,22 @@ static const cmdinfo_t close_cmd = {
.oneline = "close the current open file", .oneline = "close the current open file",
}; };
static int openfile(char *name, BlockDriver *drv, int flags, int growable, static int openfile(char *name, int flags, QDict *opts)
QDict *opts)
{ {
Error *local_err = NULL; Error *local_err = NULL;
if (qemuio_bs) { if (qemuio_blk) {
fprintf(stderr, "file open already, try 'help close'\n"); fprintf(stderr, "file open already, try 'help close'\n");
QDECREF(opts); QDECREF(opts);
return 1; return 1;
} }
qemuio_blk = blk_new_with_bs("hda", &error_abort); qemuio_blk = blk_new_open("hda", name, NULL, opts, flags, &local_err);
qemuio_bs = blk_bs(qemuio_blk); if (!qemuio_blk) {
if (growable) {
flags |= BDRV_O_PROTOCOL;
}
if (bdrv_open(&qemuio_bs, name, NULL, opts, flags, drv, &local_err) < 0) {
fprintf(stderr, "%s: can't open%s%s: %s\n", progname, fprintf(stderr, "%s: can't open%s%s: %s\n", progname,
name ? " device " : "", name ?: "", name ? " device " : "", name ?: "",
error_get_pretty(local_err)); error_get_pretty(local_err));
error_free(local_err); error_free(local_err);
blk_unref(qemuio_blk);
qemuio_bs = NULL;
qemuio_blk = NULL;
return 1; return 1;
} }
@ -96,12 +84,11 @@ static void open_help(void)
" -r, -- open file read-only\n" " -r, -- open file read-only\n"
" -s, -- use snapshot file\n" " -s, -- use snapshot file\n"
" -n, -- disable host cache\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" " -o, -- options to be given to the block driver"
"\n"); "\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 = { static const cmdinfo_t open_cmd = {
.name = "open", .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 flags = 0;
int readonly = 0; int readonly = 0;
int growable = 0;
int c; int c;
QemuOpts *qopts; QemuOpts *qopts;
QDict *opts; QDict *opts;
@ -145,9 +131,6 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
case 'r': case 'r':
readonly = 1; readonly = 1;
break; break;
case 'g':
growable = 1;
break;
case 'o': case 'o':
if (!qemu_opts_parse(&empty_opts, optarg, 0)) { if (!qemu_opts_parse(&empty_opts, optarg, 0)) {
printf("could not parse option list -- %s\n", optarg); 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); qemu_opts_reset(&empty_opts);
if (optind == argc - 1) { if (optind == argc - 1) {
return openfile(argv[optind], NULL, flags, growable, opts); return openfile(argv[optind], flags, opts);
} else if (optind == argc) { } else if (optind == argc) {
return openfile(NULL, NULL, flags, growable, opts); return openfile(NULL, flags, opts);
} else { } else {
QDECREF(opts); QDECREF(opts);
return qemuio_command_usage(&open_cmd); 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; return 1;
} }
@ -206,7 +189,6 @@ static void usage(const char *name)
" -r, --read-only export read-only\n" " -r, --read-only export read-only\n"
" -s, --snapshot use snapshot file\n" " -s, --snapshot use snapshot file\n"
" -n, --nocache disable host cache\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" " -m, --misalign misalign allocations for O_DIRECT\n"
" -k, --native-aio use kernel AIO implementation (on Linux only)\n" " -k, --native-aio use kernel AIO implementation (on Linux only)\n"
" -t, --cache=MODE use the given cache mode for the image\n" " -t, --cache=MODE use the given cache mode for the image\n"
@ -317,7 +299,7 @@ static void command_loop(void)
char *input; char *input;
for (i = 0; !done && i < ncmdline; i++) { for (i = 0; !done && i < ncmdline; i++) {
done = qemuio_command(qemuio_bs, cmdline[i]); done = qemuio_command(qemuio_blk, cmdline[i]);
} }
if (cmdline) { if (cmdline) {
g_free(cmdline); g_free(cmdline);
@ -342,7 +324,7 @@ static void command_loop(void)
if (input == NULL) { if (input == NULL) {
break; break;
} }
done = qemuio_command(qemuio_bs, input); done = qemuio_command(qemuio_blk, input);
g_free(input); g_free(input);
prompted = 0; prompted = 0;
@ -365,7 +347,6 @@ static void reenable_tty_echo(void)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int readonly = 0; int readonly = 0;
int growable = 0;
const char *sopt = "hVc:d:f:rsnmgkt:T:"; const char *sopt = "hVc:d:f:rsnmgkt:T:";
const struct option lopt[] = { const struct option lopt[] = {
{ "help", 0, NULL, 'h' }, { "help", 0, NULL, 'h' },
@ -377,7 +358,6 @@ int main(int argc, char **argv)
{ "snapshot", 0, NULL, 's' }, { "snapshot", 0, NULL, 's' },
{ "nocache", 0, NULL, 'n' }, { "nocache", 0, NULL, 'n' },
{ "misalign", 0, NULL, 'm' }, { "misalign", 0, NULL, 'm' },
{ "growable", 0, NULL, 'g' },
{ "native-aio", 0, NULL, 'k' }, { "native-aio", 0, NULL, 'k' },
{ "discard", 1, NULL, 'd' }, { "discard", 1, NULL, 'd' },
{ "cache", 1, NULL, 't' }, { "cache", 1, NULL, 't' },
@ -387,8 +367,8 @@ int main(int argc, char **argv)
int c; int c;
int opt_index = 0; int opt_index = 0;
int flags = BDRV_O_UNMAP; int flags = BDRV_O_UNMAP;
BlockDriver *drv = NULL;
Error *local_error = NULL; Error *local_error = NULL;
QDict *opts = NULL;
#ifdef CONFIG_POSIX #ifdef CONFIG_POSIX
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
@ -414,11 +394,10 @@ int main(int argc, char **argv)
} }
break; break;
case 'f': case 'f':
drv = bdrv_find_format(optarg); if (!opts) {
if (!drv) { opts = qdict_new();
error_report("Invalid format '%s'", optarg);
exit(EXIT_FAILURE);
} }
qdict_put(opts, "driver", qstring_from_str(optarg));
break; break;
case 'c': case 'c':
add_user_command(optarg); add_user_command(optarg);
@ -429,9 +408,6 @@ int main(int argc, char **argv)
case 'm': case 'm':
qemuio_misalign = true; qemuio_misalign = true;
break; break;
case 'g':
growable = 1;
break;
case 'k': case 'k':
flags |= BDRV_O_NATIVE_AIO; flags |= BDRV_O_NATIVE_AIO;
break; break;
@ -489,7 +465,7 @@ int main(int argc, char **argv)
} }
if ((argc - optind) == 1) { if ((argc - optind) == 1) {
openfile(argv[optind], drv, flags, growable, NULL); openfile(argv[optind], flags, opts);
} }
command_loop(); command_loop();

View File

@ -391,7 +391,6 @@ int main(int argc, char **argv)
{ {
BlockBackend *blk; BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
BlockDriver *drv;
off_t dev_offset = 0; off_t dev_offset = 0;
uint32_t nbdflags = 0; uint32_t nbdflags = 0;
bool disconnect = false; bool disconnect = false;
@ -434,7 +433,7 @@ int main(int argc, char **argv)
char *end; char *end;
int flags = BDRV_O_RDWR; int flags = BDRV_O_RDWR;
int partition = -1; int partition = -1;
int ret; int ret = 0;
int fd; int fd;
bool seen_cache = false; bool seen_cache = false;
bool seen_discard = false; bool seen_discard = false;
@ -445,6 +444,7 @@ int main(int argc, char **argv)
const char *fmt = NULL; const char *fmt = NULL;
Error *local_err = NULL; Error *local_err = NULL;
BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
QDict *options = NULL;
/* The client thread uses SIGTERM to interrupt the server. A signal /* The client thread uses SIGTERM to interrupt the server. A signal
* handler ensures that "qemu-nbd -v -c" exits with a nice status code. * 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); atexit(bdrv_close_all);
if (fmt) { if (fmt) {
drv = bdrv_find_format(fmt); options = qdict_new();
if (!drv) { qdict_put(options, "driver", qstring_from_str(fmt));
errx(EXIT_FAILURE, "Unknown file format '%s'", fmt);
}
} else {
drv = NULL;
} }
blk = blk_new_with_bs("hda", &error_abort);
bs = blk_bs(blk);
srcpath = argv[optind]; srcpath = argv[optind];
ret = bdrv_open(&bs, srcpath, NULL, NULL, flags, drv, &local_err); blk = blk_new_open("hda", srcpath, NULL, options, flags, &local_err);
if (ret < 0) { if (!blk) {
errno = -ret; errx(EXIT_FAILURE, "Failed to blk_new_open '%s': %s", argv[optind],
err(EXIT_FAILURE, "Failed to bdrv_open '%s': %s", argv[optind], error_get_pretty(local_err));
error_get_pretty(local_err));
} }
bs = blk_bs(blk);
if (sn_opts) { if (sn_opts) {
ret = bdrv_snapshot_load_tmp(bs, ret = bdrv_snapshot_load_tmp(bs,

View File

@ -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; int ret;
MigrationParams params = { MigrationParams params = {
@ -829,7 +829,7 @@ static int qemu_savevm_state(QEMUFile *f)
.shared = 0 .shared = 0
}; };
if (qemu_savevm_state_blocked(NULL)) { if (qemu_savevm_state_blocked(errp)) {
return -EINVAL; return -EINVAL;
} }
@ -850,6 +850,7 @@ static int qemu_savevm_state(QEMUFile *f)
} }
if (ret != 0) { if (ret != 0) {
qemu_savevm_state_cancel(); qemu_savevm_state_cancel();
error_setg_errno(errp, -ret, "Error while writing VM state");
} }
return ret; return ret;
} }
@ -1102,6 +1103,7 @@ void do_savevm(Monitor *mon, const QDict *qdict)
qemu_timeval tv; qemu_timeval tv;
struct tm tm; struct tm tm;
const char *name = qdict_get_try_str(qdict, "name"); 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 */ /* Verify if there is a device that doesn't support snapshots and is writable */
bs = NULL; bs = NULL;
@ -1160,11 +1162,12 @@ void do_savevm(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "Could not open VM state file\n"); monitor_printf(mon, "Could not open VM state file\n");
goto the_end; goto the_end;
} }
ret = qemu_savevm_state(f); ret = qemu_savevm_state(f, &local_err);
vm_state_size = qemu_ftell(f); vm_state_size = qemu_ftell(f);
qemu_fclose(f); qemu_fclose(f);
if (ret < 0) { 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; goto the_end;
} }

71
scripts/qtest.py Normal file
View File

@ -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)

View File

@ -307,9 +307,10 @@ tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a
tests/test-bitops$(EXESUF): tests/test-bitops.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/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 = $(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-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-virtio-obj-y = $(libqos-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o
libqos-usb-obj-y = $(libqos-pc-obj-y) tests/libqos/usb.o libqos-usb-obj-y = $(libqos-pc-obj-y) tests/libqos/usb.o

File diff suppressed because it is too large Load Diff

838
tests/libqos/ahci.c Normal file
View File

@ -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;
}

549
tests/libqos/ahci.h Normal file
View File

@ -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

24
tests/libqos/libqos-pc.c Normal file
View File

@ -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);
}

9
tests/libqos/libqos-pc.h Normal file
View File

@ -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

63
tests/libqos/libqos.c Normal file
View File

@ -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);
}

33
tests/libqos/libqos.h Normal file
View File

@ -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

View File

@ -32,31 +32,17 @@ void pc_alloc_uninit(QGuestAllocator *allocator)
QGuestAllocator *pc_alloc_init_flags(QAllocOpts flags) QGuestAllocator *pc_alloc_init_flags(QAllocOpts flags)
{ {
QGuestAllocator *s = g_malloc0(sizeof(*s)); QGuestAllocator *s;
uint64_t ram_size; uint64_t ram_size;
QFWCFG *fw_cfg = pc_fw_cfg_init(); 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); ram_size = qfw_cfg_get_u64(fw_cfg, FW_CFG_RAM_SIZE);
s = alloc_init_flags(flags, 1 << 20, MIN(ram_size, 0xE0000000));
/* Start at 1MB */ alloc_set_page_size(s, PAGE_SIZE);
s->start = 1 << 20;
/* Respect PCI hole */
s->end = MIN(ram_size, 0xE0000000);
/* clean-up */ /* clean-up */
g_free(fw_cfg); 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; return s;
} }

View File

@ -16,6 +16,26 @@
#include <inttypes.h> #include <inttypes.h>
#include <glib.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) static void mlist_delete(MemList *list, MemBlock *node)
{ {
g_assert(list && node); g_assert(list && node);
@ -103,6 +123,21 @@ static void mlist_coalesce(MemList *head, MemBlock *node)
} while (merge); } 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, static uint64_t mlist_fulfill(QGuestAllocator *s, MemBlock *freenode,
uint64_t size) uint64_t size)
{ {
@ -187,21 +222,6 @@ static void mlist_free(QGuestAllocator *s, uint64_t addr)
mlist_coalesce(&s->free, node); 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 * Mostly for valgrind happiness, but it does offer
* a chokepoint for debugging guest memory leaks, too. * a chokepoint for debugging guest memory leaks, too.
@ -268,3 +288,44 @@ void guest_free(QGuestAllocator *allocator, uint64_t addr)
mlist_check(allocator); 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;
}

View File

@ -17,8 +17,6 @@
#include <sys/types.h> #include <sys/types.h>
#include "qemu/queue.h" #include "qemu/queue.h"
#define MLIST_ENTNAME entries
typedef enum { typedef enum {
ALLOC_NO_FLAGS = 0x00, ALLOC_NO_FLAGS = 0x00,
ALLOC_LEAK_WARN = 0x01, ALLOC_LEAK_WARN = 0x01,
@ -26,28 +24,18 @@ typedef enum {
ALLOC_PARANOID = 0x04 ALLOC_PARANOID = 0x04
} QAllocOpts; } QAllocOpts;
typedef QTAILQ_HEAD(MemList, MemBlock) MemList; typedef struct QGuestAllocator QGuestAllocator;
typedef struct MemBlock {
QTAILQ_ENTRY(MemBlock) MLIST_ENTNAME;
uint64_t size;
uint64_t addr;
} MemBlock;
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); void alloc_uninit(QGuestAllocator *allocator);
/* Always returns page aligned values */ /* Always returns page aligned values */
uint64_t guest_alloc(QGuestAllocator *allocator, size_t size); uint64_t guest_alloc(QGuestAllocator *allocator, size_t size);
void guest_free(QGuestAllocator *allocator, uint64_t addr); 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 #endif

View File

@ -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

View File

@ -93,6 +93,7 @@ echo
run_qemu -drive file="$TEST_IMG",format=foo run_qemu -drive file="$TEST_IMG",format=foo
run_qemu -drive file="$TEST_IMG",driver=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=raw,format=qcow2
run_qemu -drive file="$TEST_IMG",driver=qcow2,format=qcow2
echo echo
echo === Overriding backing file === echo === Overriding backing file ===

View File

@ -5,43 +5,46 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR
=== Unknown option === === Unknown option ===
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt= 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 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 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 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 === === Unknown protocol option ===
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt= 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 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 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 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 === === Invalid format ===
Testing: -drive file=TEST_DIR/t.qcow2,format=foo 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 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 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 === === Overriding backing file ===
@ -55,13 +58,13 @@ ide0-hd0: TEST_DIR/t.qcow2 (qcow2)
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig 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 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 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 === === 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) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts= 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 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 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 === === With version 2 images enabling lazy refcounts must fail ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on 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 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off
QEMU X.Y.Z monitor - type 'help' for more information 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) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2 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 === === Leaving out required options ===
Testing: -drive driver=file 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 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 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 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 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 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 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 === === 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 === === Parsing protocol from file name ===
Testing: -hda foo:bar 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 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 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 Testing: -hda file:TEST_DIR/t.qcow2
QEMU X.Y.Z monitor - type 'help' for more information 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) qququiquit (qemu) qququiquit
Testing: -drive file.filename=file:TEST_DIR/t.qcow2 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 === === Snapshot mode ===

View File

@ -21,9 +21,9 @@ QMP_VERSION
{"return": {}} {"return": {}}
{"error": {"class": "GenericError", "desc": "Device with id 'disk' already exists"}} {"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": "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": "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": "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=disk3 is conflicting with a device id"}}
{"return": {}} {"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
@ -57,7 +57,7 @@ QMP_VERSION
Testing: Testing:
QMP_VERSION QMP_VERSION
{"return": {}} {"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": {}} {"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}

114
tests/qemu-iotests/093 Executable file
View File

@ -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"])

View File

@ -0,0 +1,5 @@
..
----------------------------------------------------------------------
Ran 2 tests
OK

81
tests/qemu-iotests/094 Executable file
View File

@ -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

View File

@ -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

View File

@ -1,8 +1,8 @@
#!/bin/bash #!/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 # 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 # it under the terms of the GNU General Public License as published by
@ -19,18 +19,19 @@
# #
# creator # creator
owner=hch@lst.de owner=mreitz@redhat.com
seq=`basename $0` seq="$(basename $0)"
echo "QA output created by $seq" echo "QA output created by $seq"
here=`pwd` here="$PWD"
tmp=/tmp/$$ tmp=/tmp/$$
status=1 # failure is the default! status=1 # failure is the default!
_cleanup() _cleanup()
{ {
_cleanup_test_img _cleanup_test_img
rm -f "$SRC_IMG"
} }
trap "_cleanup; exit \$status" 0 1 2 3 15 trap "_cleanup; exit \$status" 0 1 2 3 15
@ -39,35 +40,23 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter . ./common.filter
_supported_fmt raw _supported_fmt raw
_supported_proto file sheepdog nfs _supported_proto nbd
_supported_os Linux _supported_os Linux
SRC_IMG="$TEST_DIR/source.$IMGFMT"
# No -f, use probing for the protocol driver _make_test_img 1M
QEMU_IO_PROTO="$QEMU_IO_PROG -g --cache $CACHEMODE" $QEMU_IMG create -f $IMGFMT "$SRC_IMG" 1M | _filter_img_create
size=128M $QEMU_IO -c 'write -P 42 0 1M' "$SRC_IMG" | _filter_qemu_io
_make_test_img $size
echo $QEMU_IMG convert -n -f $IMGFMT -O raw "$SRC_IMG" "$TEST_IMG"
echo "== reading at EOF =="
$QEMU_IO_PROTO -c "read -P 0 $size 512" "$TEST_IMG" | _filter_qemu_io
echo $QEMU_IO -c 'read -P 42 0 1M' "$TEST_IMG" | _filter_qemu_io
echo "== reading far past EOF =="
$QEMU_IO_PROTO -c "read -P 0 256M 512" "$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 # success, all done
echo "*** done" echo
echo '*** done'
rm -f $seq.full rm -f $seq.full
status=0 status=0

View File

@ -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

View File

@ -187,13 +187,23 @@ function _launch_qemu()
# Silenty kills the QEMU process # 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() function _cleanup_qemu()
{ {
# QEMU_PID[], QEMU_IN[], QEMU_OUT[] all use same indices # QEMU_PID[], QEMU_IN[], QEMU_OUT[] all use same indices
for i in "${!QEMU_OUT[@]}" for i in "${!QEMU_OUT[@]}"
do 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 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}" rm -f "${QEMU_FIFO_IN}_${i}" "${QEMU_FIFO_OUT}_${i}"
eval "exec ${QEMU_IN[$i]}<&-" # close file descriptors eval "exec ${QEMU_IN[$i]}<&-" # close file descriptors
eval "exec ${QEMU_OUT[$i]}<&-" eval "exec ${QEMU_OUT[$i]}<&-"

View File

@ -22,7 +22,7 @@
013 rw auto 013 rw auto
014 rw auto 014 rw auto
015 rw snapshot auto 015 rw snapshot auto
016 rw auto quick # 016 was removed, do not reuse
017 rw backing auto quick 017 rw backing auto quick
018 rw backing auto quick 018 rw backing auto quick
019 rw backing auto quick 019 rw backing auto quick
@ -99,6 +99,8 @@
090 rw auto quick 090 rw auto quick
091 rw auto 091 rw auto
092 rw auto quick 092 rw auto quick
093 auto
094 rw auto quick
095 rw auto quick 095 rw auto quick
097 rw auto backing 097 rw auto backing
098 rw auto backing quick 098 rw auto backing quick
@ -117,3 +119,4 @@
113 rw auto quick 113 rw auto quick
114 rw auto quick 114 rw auto quick
116 rw auto quick 116 rw auto quick
123 rw auto quick

View File

@ -21,8 +21,11 @@ import re
import subprocess import subprocess
import string import string
import unittest 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 qmp
import qtest
import struct import struct
__all__ = ['imgfmt', 'imgproto', 'test_dir' 'qemu_img', 'qemu_io', __all__ = ['imgfmt', 'imgproto', 'test_dir' 'qemu_img', 'qemu_io',
@ -81,10 +84,12 @@ class VM(object):
def __init__(self): def __init__(self):
self._monitor_path = os.path.join(test_dir, 'qemu-mon.%d' % os.getpid()) 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._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', self._args = qemu_args + ['-chardev',
'socket,id=mon,path=' + self._monitor_path, 'socket,id=mon,path=' + self._monitor_path,
'-mon', 'chardev=mon,mode=control', '-mon', 'chardev=mon,mode=control',
'-qtest', 'stdio', '-machine', 'accel=qtest', '-qtest', 'unix:path=' + self._qtest_path,
'-machine', 'accel=qtest',
'-display', 'none', '-vga', 'none'] '-display', 'none', '-vga', 'none']
self._num_drives = 0 self._num_drives = 0
@ -160,9 +165,11 @@ class VM(object):
qemulog = open(self._qemu_log_path, 'wb') qemulog = open(self._qemu_log_path, 'wb')
try: try:
self._qmp = qmp.QEMUMonitorProtocol(self._monitor_path, server=True) 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, self._popen = subprocess.Popen(self._args, stdin=devnull, stdout=qemulog,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
self._qmp.accept() self._qmp.accept()
self._qtest.accept()
except: except:
os.remove(self._monitor_path) os.remove(self._monitor_path)
raise raise
@ -173,18 +180,26 @@ class VM(object):
self._qmp.cmd('quit') self._qmp.cmd('quit')
self._popen.wait() self._popen.wait()
os.remove(self._monitor_path) os.remove(self._monitor_path)
os.remove(self._qtest_path)
os.remove(self._qemu_log_path) os.remove(self._qemu_log_path)
self._popen = None self._popen = None
underscore_to_dash = string.maketrans('_', '-') 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''' '''Invoke a QMP command and return the result dict'''
qmp_args = dict() qmp_args = dict()
for k in args.keys(): 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) 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): def get_qmp_event(self, wait=False):
'''Poll for one queued QMP events and return it''' '''Poll for one queued QMP events and return it'''
return self._qmp.pull_event(wait=wait) return self._qmp.pull_event(wait=wait)