-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1 iQEcBAABAgAGBQJXZ/gkAAoJEJykq7OBq3PIx/4H/j1ef3rJnGJqwHeGZnYOvKyP hRfaeg7OW0Tv6MWDltEsGUvMe+lx20kRrxpob4TyRWFNg3iM1a+b2XHjIbXMBejK r+tU2iBPS0Vv5OCK2bOX+UiJsukfOA1blnHEiZLGdltVdMOA8QJ8fzM/z7Etb9rK 38js98XGqJ1pv0zxDiAkVW3xtqjbAUZYL9mrdfHXchgjWjryvlnL5Wj134ozw3E8 UOAJnOEEagT52u6KeT7CvJdGmxdmZ32THpYUlMgOBp9xTomp8zyxp3JfFJhcfYEd GHoTfT4aa0sOUC4jZbvEO3TapOIuSs9eGhbstlOTUc/YitViOhoBLoLD4VIdFtQ= =LOUh -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/stefanha/tags/block-pull-request' into staging # gpg: Signature made Mon 20 Jun 2016 15:05:24 BST # gpg: using RSA key 0x9CA4ABB381AB73C8 # gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>" # gpg: aka "Stefan Hajnoczi <stefanha@gmail.com>" # Primary key fingerprint: 8695 A8BF D3F9 7CDA AC35 775A 9CA4 ABB3 81AB 73C8 * remotes/stefanha/tags/block-pull-request: backup: follow AioContext change gracefully mirror: follow AioContext change gracefully blockjob: add AioContext attached callback block: use safe iteration over AioContext notifiers blockjob: add block_job_get_aio_context() blockjob: add pause points blockjob: rename block_job_is_paused() blockjob: move iostatus reset out of block_job_enter() block: process before_write_notifiers in bdrv_co_discard block: fix race in bdrv_co_discard with drive-mirror block: fixed BdrvTrackedRequest filling in bdrv_co_discard libqos: add qvirtqueue_cleanup() libqos: drop duplicated virtio_pci.h definitions libqos: drop duplicated virtio_scsi.h definitions libqos: drop duplicated virtio_blk.h definitions libqos: drop duplicated virtio_vring.h structs libqos: drop duplicated virtio_ring.h bit definitions libqos: drop duplicated virtio_config.h definitions libqos: drop duplicated PCI vendor ID definition libqos: use virtio_ids.h for device ID definitions Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
fd2590bccc
46
block.c
46
block.c
@ -3609,18 +3609,34 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs)
|
|||||||
return bs->aio_context;
|
return bs->aio_context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bdrv_do_remove_aio_context_notifier(BdrvAioNotifier *ban)
|
||||||
|
{
|
||||||
|
QLIST_REMOVE(ban, list);
|
||||||
|
g_free(ban);
|
||||||
|
}
|
||||||
|
|
||||||
void bdrv_detach_aio_context(BlockDriverState *bs)
|
void bdrv_detach_aio_context(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BdrvAioNotifier *baf;
|
BdrvAioNotifier *baf, *baf_tmp;
|
||||||
BdrvChild *child;
|
BdrvChild *child;
|
||||||
|
|
||||||
if (!bs->drv) {
|
if (!bs->drv) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QLIST_FOREACH(baf, &bs->aio_notifiers, list) {
|
assert(!bs->walking_aio_notifiers);
|
||||||
baf->detach_aio_context(baf->opaque);
|
bs->walking_aio_notifiers = true;
|
||||||
|
QLIST_FOREACH_SAFE(baf, &bs->aio_notifiers, list, baf_tmp) {
|
||||||
|
if (baf->deleted) {
|
||||||
|
bdrv_do_remove_aio_context_notifier(baf);
|
||||||
|
} else {
|
||||||
|
baf->detach_aio_context(baf->opaque);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
/* Never mind iterating again to check for ->deleted. bdrv_close() will
|
||||||
|
* remove remaining aio notifiers if we aren't called again.
|
||||||
|
*/
|
||||||
|
bs->walking_aio_notifiers = false;
|
||||||
|
|
||||||
if (bs->drv->bdrv_detach_aio_context) {
|
if (bs->drv->bdrv_detach_aio_context) {
|
||||||
bs->drv->bdrv_detach_aio_context(bs);
|
bs->drv->bdrv_detach_aio_context(bs);
|
||||||
@ -3635,7 +3651,7 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
|
|||||||
void bdrv_attach_aio_context(BlockDriverState *bs,
|
void bdrv_attach_aio_context(BlockDriverState *bs,
|
||||||
AioContext *new_context)
|
AioContext *new_context)
|
||||||
{
|
{
|
||||||
BdrvAioNotifier *ban;
|
BdrvAioNotifier *ban, *ban_tmp;
|
||||||
BdrvChild *child;
|
BdrvChild *child;
|
||||||
|
|
||||||
if (!bs->drv) {
|
if (!bs->drv) {
|
||||||
@ -3651,9 +3667,16 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
|
|||||||
bs->drv->bdrv_attach_aio_context(bs, new_context);
|
bs->drv->bdrv_attach_aio_context(bs, new_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
QLIST_FOREACH(ban, &bs->aio_notifiers, list) {
|
assert(!bs->walking_aio_notifiers);
|
||||||
ban->attached_aio_context(new_context, ban->opaque);
|
bs->walking_aio_notifiers = true;
|
||||||
|
QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_tmp) {
|
||||||
|
if (ban->deleted) {
|
||||||
|
bdrv_do_remove_aio_context_notifier(ban);
|
||||||
|
} else {
|
||||||
|
ban->attached_aio_context(new_context, ban->opaque);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
bs->walking_aio_notifiers = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
|
void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
|
||||||
@ -3695,11 +3718,14 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
|
|||||||
QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_next) {
|
QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_next) {
|
||||||
if (ban->attached_aio_context == attached_aio_context &&
|
if (ban->attached_aio_context == attached_aio_context &&
|
||||||
ban->detach_aio_context == detach_aio_context &&
|
ban->detach_aio_context == detach_aio_context &&
|
||||||
ban->opaque == opaque)
|
ban->opaque == opaque &&
|
||||||
|
ban->deleted == false)
|
||||||
{
|
{
|
||||||
QLIST_REMOVE(ban, list);
|
if (bs->walking_aio_notifiers) {
|
||||||
g_free(ban);
|
ban->deleted = true;
|
||||||
|
} else {
|
||||||
|
bdrv_do_remove_aio_context_notifier(ban);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,12 +246,20 @@ static void backup_abort(BlockJob *job)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void backup_attached_aio_context(BlockJob *job, AioContext *aio_context)
|
||||||
|
{
|
||||||
|
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||||
|
|
||||||
|
blk_set_aio_context(s->target, aio_context);
|
||||||
|
}
|
||||||
|
|
||||||
static const BlockJobDriver backup_job_driver = {
|
static const BlockJobDriver backup_job_driver = {
|
||||||
.instance_size = sizeof(BackupBlockJob),
|
.instance_size = sizeof(BackupBlockJob),
|
||||||
.job_type = BLOCK_JOB_TYPE_BACKUP,
|
.job_type = BLOCK_JOB_TYPE_BACKUP,
|
||||||
.set_speed = backup_set_speed,
|
.set_speed = backup_set_speed,
|
||||||
.commit = backup_commit,
|
.commit = backup_commit,
|
||||||
.abort = backup_abort,
|
.abort = backup_abort,
|
||||||
|
.attached_aio_context = backup_attached_aio_context,
|
||||||
};
|
};
|
||||||
|
|
||||||
static BlockErrorAction backup_error_action(BackupBlockJob *job,
|
static BlockErrorAction backup_error_action(BackupBlockJob *job,
|
||||||
@ -392,9 +400,7 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
while (!block_job_is_cancelled(&job->common)) {
|
while (!block_job_is_cancelled(&job->common)) {
|
||||||
/* Yield until the job is cancelled. We just let our before_write
|
/* Yield until the job is cancelled. We just let our before_write
|
||||||
* notify callback service CoW requests. */
|
* notify callback service CoW requests. */
|
||||||
job->common.busy = false;
|
block_job_yield(&job->common);
|
||||||
qemu_coroutine_yield();
|
|
||||||
job->common.busy = true;
|
|
||||||
}
|
}
|
||||||
} else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
} else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||||
ret = backup_run_incremental(job);
|
ret = backup_run_incremental(job);
|
||||||
|
12
block/io.c
12
block/io.c
@ -2347,9 +2347,13 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
tracked_request_begin(&req, bs, sector_num, nb_sectors,
|
tracked_request_begin(&req, bs, sector_num << BDRV_SECTOR_BITS,
|
||||||
BDRV_TRACKED_DISCARD);
|
nb_sectors << BDRV_SECTOR_BITS, BDRV_TRACKED_DISCARD);
|
||||||
bdrv_set_dirty(bs, sector_num, nb_sectors);
|
|
||||||
|
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, &req);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
max_discard = MIN_NON_ZERO(bs->bl.max_discard, BDRV_REQUEST_MAX_SECTORS);
|
max_discard = MIN_NON_ZERO(bs->bl.max_discard, BDRV_REQUEST_MAX_SECTORS);
|
||||||
while (nb_sectors > 0) {
|
while (nb_sectors > 0) {
|
||||||
@ -2398,6 +2402,8 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
|||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
out:
|
out:
|
||||||
|
bdrv_set_dirty(bs, req.offset >> BDRV_SECTOR_BITS,
|
||||||
|
req.bytes >> BDRV_SECTOR_BITS);
|
||||||
tracked_request_end(&req);
|
tracked_request_end(&req);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -331,6 +331,8 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|||||||
mirror_wait_for_io(s);
|
mirror_wait_for_io(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
block_job_pause_point(&s->common);
|
||||||
|
|
||||||
/* Find the number of consective dirty chunks following the first dirty
|
/* Find the number of consective dirty chunks following the first dirty
|
||||||
* one, and wait for in flight requests in them. */
|
* one, and wait for in flight requests in them. */
|
||||||
while (nb_chunks * sectors_per_chunk < (s->buf_size >> BDRV_SECTOR_BITS)) {
|
while (nb_chunks * sectors_per_chunk < (s->buf_size >> BDRV_SECTOR_BITS)) {
|
||||||
@ -582,6 +584,8 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||||||
if (now - last_pause_ns > SLICE_TIME) {
|
if (now - last_pause_ns > SLICE_TIME) {
|
||||||
last_pause_ns = now;
|
last_pause_ns = now;
|
||||||
block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, 0);
|
block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, 0);
|
||||||
|
} else {
|
||||||
|
block_job_pause_point(&s->common);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block_job_is_cancelled(&s->common)) {
|
if (block_job_is_cancelled(&s->common)) {
|
||||||
@ -613,6 +617,8 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||||||
goto immediate_exit;
|
goto immediate_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
block_job_pause_point(&s->common);
|
||||||
|
|
||||||
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
|
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
|
||||||
/* s->common.offset contains the number of bytes already processed so
|
/* s->common.offset contains the number of bytes already processed so
|
||||||
* far, cnt is the number of dirty sectors remaining and
|
* far, cnt is the number of dirty sectors remaining and
|
||||||
@ -795,18 +801,39 @@ static void mirror_complete(BlockJob *job, Error **errp)
|
|||||||
block_job_enter(&s->common);
|
block_job_enter(&s->common);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* There is no matching mirror_resume() because mirror_run() will begin
|
||||||
|
* iterating again when the job is resumed.
|
||||||
|
*/
|
||||||
|
static void coroutine_fn mirror_pause(BlockJob *job)
|
||||||
|
{
|
||||||
|
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||||
|
|
||||||
|
mirror_drain(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mirror_attached_aio_context(BlockJob *job, AioContext *new_context)
|
||||||
|
{
|
||||||
|
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||||
|
|
||||||
|
blk_set_aio_context(s->target, new_context);
|
||||||
|
}
|
||||||
|
|
||||||
static const BlockJobDriver mirror_job_driver = {
|
static const BlockJobDriver mirror_job_driver = {
|
||||||
.instance_size = sizeof(MirrorBlockJob),
|
.instance_size = sizeof(MirrorBlockJob),
|
||||||
.job_type = BLOCK_JOB_TYPE_MIRROR,
|
.job_type = BLOCK_JOB_TYPE_MIRROR,
|
||||||
.set_speed = mirror_set_speed,
|
.set_speed = mirror_set_speed,
|
||||||
.complete = mirror_complete,
|
.complete = mirror_complete,
|
||||||
|
.pause = mirror_pause,
|
||||||
|
.attached_aio_context = mirror_attached_aio_context,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const BlockJobDriver commit_active_job_driver = {
|
static const BlockJobDriver commit_active_job_driver = {
|
||||||
.instance_size = sizeof(MirrorBlockJob),
|
.instance_size = sizeof(MirrorBlockJob),
|
||||||
.job_type = BLOCK_JOB_TYPE_COMMIT,
|
.job_type = BLOCK_JOB_TYPE_COMMIT,
|
||||||
.set_speed = mirror_set_speed,
|
.set_speed = mirror_set_speed,
|
||||||
.complete = mirror_complete,
|
.complete = mirror_complete,
|
||||||
|
.pause = mirror_pause,
|
||||||
|
.attached_aio_context = mirror_attached_aio_context,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||||
|
@ -3811,6 +3811,7 @@ void qmp_block_job_resume(const char *device, Error **errp)
|
|||||||
|
|
||||||
job->user_paused = false;
|
job->user_paused = false;
|
||||||
trace_qmp_block_job_resume(job);
|
trace_qmp_block_job_resume(job);
|
||||||
|
block_job_iostatus_reset(job);
|
||||||
block_job_resume(job);
|
block_job_resume(job);
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
}
|
}
|
||||||
|
97
blockjob.c
97
blockjob.c
@ -60,6 +60,49 @@ BlockJob *block_job_next(BlockJob *job)
|
|||||||
return QLIST_NEXT(job, job_list);
|
return QLIST_NEXT(job, job_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Normally the job runs in its BlockBackend's AioContext. The exception is
|
||||||
|
* block_job_defer_to_main_loop() where it runs in the QEMU main loop. Code
|
||||||
|
* that supports both cases uses this helper function.
|
||||||
|
*/
|
||||||
|
static AioContext *block_job_get_aio_context(BlockJob *job)
|
||||||
|
{
|
||||||
|
return job->deferred_to_main_loop ?
|
||||||
|
qemu_get_aio_context() :
|
||||||
|
blk_get_aio_context(job->blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_attached_aio_context(AioContext *new_context,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
BlockJob *job = opaque;
|
||||||
|
|
||||||
|
if (job->driver->attached_aio_context) {
|
||||||
|
job->driver->attached_aio_context(job, new_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
block_job_resume(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_detach_aio_context(void *opaque)
|
||||||
|
{
|
||||||
|
BlockJob *job = opaque;
|
||||||
|
|
||||||
|
/* In case the job terminates during aio_poll()... */
|
||||||
|
block_job_ref(job);
|
||||||
|
|
||||||
|
block_job_pause(job);
|
||||||
|
|
||||||
|
if (!job->paused) {
|
||||||
|
/* If job is !job->busy this kicks it into the next pause point. */
|
||||||
|
block_job_enter(job);
|
||||||
|
}
|
||||||
|
while (!job->paused && !job->completed) {
|
||||||
|
aio_poll(block_job_get_aio_context(job), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
block_job_unref(job);
|
||||||
|
}
|
||||||
|
|
||||||
void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||||
int64_t speed, BlockCompletionFunc *cb,
|
int64_t speed, BlockCompletionFunc *cb,
|
||||||
void *opaque, Error **errp)
|
void *opaque, Error **errp)
|
||||||
@ -92,6 +135,9 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
|||||||
|
|
||||||
QLIST_INSERT_HEAD(&block_jobs, job, job_list);
|
QLIST_INSERT_HEAD(&block_jobs, job, job_list);
|
||||||
|
|
||||||
|
blk_add_aio_context_notifier(blk, block_job_attached_aio_context,
|
||||||
|
block_job_detach_aio_context, job);
|
||||||
|
|
||||||
/* Only set speed when necessary to avoid NotSupported error */
|
/* Only set speed when necessary to avoid NotSupported error */
|
||||||
if (speed != 0) {
|
if (speed != 0) {
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
@ -117,6 +163,9 @@ void block_job_unref(BlockJob *job)
|
|||||||
BlockDriverState *bs = blk_bs(job->blk);
|
BlockDriverState *bs = blk_bs(job->blk);
|
||||||
bs->job = NULL;
|
bs->job = NULL;
|
||||||
bdrv_op_unblock_all(bs, job->blocker);
|
bdrv_op_unblock_all(bs, job->blocker);
|
||||||
|
blk_remove_aio_context_notifier(job->blk,
|
||||||
|
block_job_attached_aio_context,
|
||||||
|
block_job_detach_aio_context, job);
|
||||||
blk_unref(job->blk);
|
blk_unref(job->blk);
|
||||||
error_free(job->blocker);
|
error_free(job->blocker);
|
||||||
g_free(job->id);
|
g_free(job->id);
|
||||||
@ -252,11 +301,37 @@ void block_job_pause(BlockJob *job)
|
|||||||
job->pause_count++;
|
job->pause_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool block_job_is_paused(BlockJob *job)
|
static bool block_job_should_pause(BlockJob *job)
|
||||||
{
|
{
|
||||||
return job->pause_count > 0;
|
return job->pause_count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void coroutine_fn block_job_pause_point(BlockJob *job)
|
||||||
|
{
|
||||||
|
if (!block_job_should_pause(job)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (block_job_is_cancelled(job)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (job->driver->pause) {
|
||||||
|
job->driver->pause(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block_job_should_pause(job) && !block_job_is_cancelled(job)) {
|
||||||
|
job->paused = true;
|
||||||
|
job->busy = false;
|
||||||
|
qemu_coroutine_yield(); /* wait for block_job_resume() */
|
||||||
|
job->busy = true;
|
||||||
|
job->paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (job->driver->resume) {
|
||||||
|
job->driver->resume(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void block_job_resume(BlockJob *job)
|
void block_job_resume(BlockJob *job)
|
||||||
{
|
{
|
||||||
assert(job->pause_count > 0);
|
assert(job->pause_count > 0);
|
||||||
@ -269,7 +344,6 @@ void block_job_resume(BlockJob *job)
|
|||||||
|
|
||||||
void block_job_enter(BlockJob *job)
|
void block_job_enter(BlockJob *job)
|
||||||
{
|
{
|
||||||
block_job_iostatus_reset(job);
|
|
||||||
if (job->co && !job->busy) {
|
if (job->co && !job->busy) {
|
||||||
qemu_coroutine_enter(job->co, NULL);
|
qemu_coroutine_enter(job->co, NULL);
|
||||||
}
|
}
|
||||||
@ -278,6 +352,7 @@ void block_job_enter(BlockJob *job)
|
|||||||
void block_job_cancel(BlockJob *job)
|
void block_job_cancel(BlockJob *job)
|
||||||
{
|
{
|
||||||
job->cancelled = true;
|
job->cancelled = true;
|
||||||
|
block_job_iostatus_reset(job);
|
||||||
block_job_enter(job);
|
block_job_enter(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,9 +386,7 @@ static int block_job_finish_sync(BlockJob *job,
|
|||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
while (!job->completed) {
|
while (!job->completed) {
|
||||||
aio_poll(job->deferred_to_main_loop ? qemu_get_aio_context() :
|
aio_poll(block_job_get_aio_context(job), true);
|
||||||
blk_get_aio_context(job->blk),
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
|
ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
|
||||||
block_job_unref(job);
|
block_job_unref(job);
|
||||||
@ -361,14 +434,12 @@ void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
|
|||||||
}
|
}
|
||||||
|
|
||||||
job->busy = false;
|
job->busy = false;
|
||||||
if (!block_job_is_paused(job)) {
|
if (!block_job_should_pause(job)) {
|
||||||
co_aio_sleep_ns(blk_get_aio_context(job->blk), type, ns);
|
co_aio_sleep_ns(blk_get_aio_context(job->blk), type, ns);
|
||||||
}
|
}
|
||||||
/* The job can be paused while sleeping, so check this again */
|
|
||||||
if (block_job_is_paused(job)) {
|
|
||||||
qemu_coroutine_yield();
|
|
||||||
}
|
|
||||||
job->busy = true;
|
job->busy = true;
|
||||||
|
|
||||||
|
block_job_pause_point(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_job_yield(BlockJob *job)
|
void block_job_yield(BlockJob *job)
|
||||||
@ -381,8 +452,12 @@ void block_job_yield(BlockJob *job)
|
|||||||
}
|
}
|
||||||
|
|
||||||
job->busy = false;
|
job->busy = false;
|
||||||
qemu_coroutine_yield();
|
if (!block_job_should_pause(job)) {
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
}
|
||||||
job->busy = true;
|
job->busy = true;
|
||||||
|
|
||||||
|
block_job_pause_point(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockJobInfo *block_job_query(BlockJob *job)
|
BlockJobInfo *block_job_query(BlockJob *job)
|
||||||
|
@ -361,6 +361,7 @@ typedef struct BdrvAioNotifier {
|
|||||||
void (*detach_aio_context)(void *opaque);
|
void (*detach_aio_context)(void *opaque);
|
||||||
|
|
||||||
void *opaque;
|
void *opaque;
|
||||||
|
bool deleted;
|
||||||
|
|
||||||
QLIST_ENTRY(BdrvAioNotifier) list;
|
QLIST_ENTRY(BdrvAioNotifier) list;
|
||||||
} BdrvAioNotifier;
|
} BdrvAioNotifier;
|
||||||
@ -427,6 +428,7 @@ struct BlockDriverState {
|
|||||||
* BDS may register themselves in this list to be notified of changes
|
* BDS may register themselves in this list to be notified of changes
|
||||||
* regarding this BDS's context */
|
* regarding this BDS's context */
|
||||||
QLIST_HEAD(, BdrvAioNotifier) aio_notifiers;
|
QLIST_HEAD(, BdrvAioNotifier) aio_notifiers;
|
||||||
|
bool walking_aio_notifiers; /* to make removal during iteration safe */
|
||||||
|
|
||||||
char filename[PATH_MAX];
|
char filename[PATH_MAX];
|
||||||
char backing_file[PATH_MAX]; /* if non zero, the image is a diff of
|
char backing_file[PATH_MAX]; /* if non zero, the image is a diff of
|
||||||
|
@ -70,6 +70,27 @@ typedef struct BlockJobDriver {
|
|||||||
* never both.
|
* never both.
|
||||||
*/
|
*/
|
||||||
void (*abort)(BlockJob *job);
|
void (*abort)(BlockJob *job);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the callback is not NULL, it will be invoked when the job transitions
|
||||||
|
* into the paused state. Paused jobs must not perform any asynchronous
|
||||||
|
* I/O or event loop activity. This callback is used to quiesce jobs.
|
||||||
|
*/
|
||||||
|
void coroutine_fn (*pause)(BlockJob *job);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the callback is not NULL, it will be invoked when the job transitions
|
||||||
|
* out of the paused state. Any asynchronous I/O or event loop activity
|
||||||
|
* should be restarted from this callback.
|
||||||
|
*/
|
||||||
|
void coroutine_fn (*resume)(BlockJob *job);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the callback is not NULL, it will be invoked before the job is
|
||||||
|
* resumed in a new AioContext. This is the place to move any resources
|
||||||
|
* besides job->blk to the new AioContext.
|
||||||
|
*/
|
||||||
|
void (*attached_aio_context)(BlockJob *job, AioContext *new_context);
|
||||||
} BlockJobDriver;
|
} BlockJobDriver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,12 +140,18 @@ struct BlockJob {
|
|||||||
bool user_paused;
|
bool user_paused;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to false by the job while it is in a quiescent state, where
|
* Set to false by the job while the coroutine has yielded and may be
|
||||||
* no I/O is pending and the job has yielded on any condition
|
* re-entered by block_job_enter(). There may still be I/O or event loop
|
||||||
* that is not detected by #aio_poll, such as a timer.
|
* activity pending.
|
||||||
*/
|
*/
|
||||||
bool busy;
|
bool busy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to true by the job while it is in a quiescent state, where
|
||||||
|
* no I/O or event loop activity is pending.
|
||||||
|
*/
|
||||||
|
bool paused;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to true when the job is ready to be completed.
|
* Set to true when the job is ready to be completed.
|
||||||
*/
|
*/
|
||||||
@ -298,6 +325,15 @@ bool block_job_is_cancelled(BlockJob *job);
|
|||||||
*/
|
*/
|
||||||
BlockJobInfo *block_job_query(BlockJob *job);
|
BlockJobInfo *block_job_query(BlockJob *job);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* block_job_pause_point:
|
||||||
|
* @job: The job that is ready to pause.
|
||||||
|
*
|
||||||
|
* Pause now if block_job_pause() has been called. Block jobs that perform
|
||||||
|
* lots of I/O must call this between requests so that the job can be paused.
|
||||||
|
*/
|
||||||
|
void coroutine_fn block_job_pause_point(BlockJob *job);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* block_job_pause:
|
* block_job_pause:
|
||||||
* @job: The job to be paused.
|
* @job: The job to be paused.
|
||||||
@ -347,15 +383,6 @@ void block_job_event_completed(BlockJob *job, const char *msg);
|
|||||||
*/
|
*/
|
||||||
void block_job_event_ready(BlockJob *job);
|
void block_job_event_ready(BlockJob *job);
|
||||||
|
|
||||||
/**
|
|
||||||
* block_job_is_paused:
|
|
||||||
* @job: The job being queried.
|
|
||||||
*
|
|
||||||
* Returns whether the job is currently paused, or will pause
|
|
||||||
* as soon as it reaches a sleeping point.
|
|
||||||
*/
|
|
||||||
bool block_job_is_paused(BlockJob *job);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* block_job_cancel_sync:
|
* block_job_cancel_sync:
|
||||||
* @job: The job to be canceled.
|
* @job: The job to be canceled.
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "libqos/virtio-mmio.h"
|
#include "libqos/virtio-mmio.h"
|
||||||
#include "libqos/malloc.h"
|
#include "libqos/malloc.h"
|
||||||
#include "libqos/malloc-generic.h"
|
#include "libqos/malloc-generic.h"
|
||||||
|
#include "standard-headers/linux/virtio_ring.h"
|
||||||
|
|
||||||
static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t addr)
|
static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t addr)
|
||||||
{
|
{
|
||||||
@ -135,8 +136,8 @@ static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d,
|
|||||||
vq->free_head = 0;
|
vq->free_head = 0;
|
||||||
vq->num_free = vq->size;
|
vq->num_free = vq->size;
|
||||||
vq->align = dev->page_size;
|
vq->align = dev->page_size;
|
||||||
vq->indirect = (dev->features & QVIRTIO_F_RING_INDIRECT_DESC) != 0;
|
vq->indirect = (dev->features & (1u << VIRTIO_RING_F_INDIRECT_DESC)) != 0;
|
||||||
vq->event = (dev->features & QVIRTIO_F_RING_EVENT_IDX) != 0;
|
vq->event = (dev->features & (1u << VIRTIO_RING_F_EVENT_IDX)) != 0;
|
||||||
|
|
||||||
writel(dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size);
|
writel(dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size);
|
||||||
|
|
||||||
@ -153,6 +154,13 @@ static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d,
|
|||||||
return vq;
|
return vq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qvirtio_mmio_virtqueue_cleanup(QVirtQueue *vq,
|
||||||
|
QGuestAllocator *alloc)
|
||||||
|
{
|
||||||
|
guest_free(alloc, vq->desc);
|
||||||
|
g_free(vq);
|
||||||
|
}
|
||||||
|
|
||||||
static void qvirtio_mmio_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
|
static void qvirtio_mmio_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
|
||||||
{
|
{
|
||||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||||
@ -175,6 +183,7 @@ const QVirtioBus qvirtio_mmio = {
|
|||||||
.get_queue_size = qvirtio_mmio_get_queue_size,
|
.get_queue_size = qvirtio_mmio_get_queue_size,
|
||||||
.set_queue_address = qvirtio_mmio_set_queue_address,
|
.set_queue_address = qvirtio_mmio_set_queue_address,
|
||||||
.virtqueue_setup = qvirtio_mmio_virtqueue_setup,
|
.virtqueue_setup = qvirtio_mmio_virtqueue_setup,
|
||||||
|
.virtqueue_cleanup = qvirtio_mmio_virtqueue_cleanup,
|
||||||
.virtqueue_kick = qvirtio_mmio_virtqueue_kick,
|
.virtqueue_kick = qvirtio_mmio_virtqueue_kick,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,7 +15,10 @@
|
|||||||
#include "libqos/pci-pc.h"
|
#include "libqos/pci-pc.h"
|
||||||
#include "libqos/malloc.h"
|
#include "libqos/malloc.h"
|
||||||
#include "libqos/malloc-pc.h"
|
#include "libqos/malloc-pc.h"
|
||||||
|
#include "standard-headers/linux/virtio_ring.h"
|
||||||
|
#include "standard-headers/linux/virtio_pci.h"
|
||||||
|
|
||||||
|
#include "hw/pci/pci.h"
|
||||||
#include "hw/pci/pci_regs.h"
|
#include "hw/pci/pci_regs.h"
|
||||||
|
|
||||||
typedef struct QVirtioPCIForeachData {
|
typedef struct QVirtioPCIForeachData {
|
||||||
@ -101,31 +104,31 @@ static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t addr)
|
|||||||
static uint32_t qvirtio_pci_get_features(QVirtioDevice *d)
|
static uint32_t qvirtio_pci_get_features(QVirtioDevice *d)
|
||||||
{
|
{
|
||||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||||
return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_FEATURES);
|
return qpci_io_readl(dev->pdev, dev->addr + VIRTIO_PCI_HOST_FEATURES);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features)
|
static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features)
|
||||||
{
|
{
|
||||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||||
qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_PCI_GUEST_FEATURES, features);
|
qpci_io_writel(dev->pdev, dev->addr + VIRTIO_PCI_GUEST_FEATURES, features);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d)
|
static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d)
|
||||||
{
|
{
|
||||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||||
return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_PCI_GUEST_FEATURES);
|
return qpci_io_readl(dev->pdev, dev->addr + VIRTIO_PCI_GUEST_FEATURES);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t qvirtio_pci_get_status(QVirtioDevice *d)
|
static uint8_t qvirtio_pci_get_status(QVirtioDevice *d)
|
||||||
{
|
{
|
||||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||||
return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_STATUS);
|
return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status)
|
static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status)
|
||||||
{
|
{
|
||||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||||
qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_STATUS, status);
|
qpci_io_writeb(dev->pdev, dev->addr + VIRTIO_PCI_STATUS, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
|
static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
|
||||||
@ -149,7 +152,7 @@ static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_ISR_STATUS) & 1;
|
return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_ISR) & 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,26 +176,26 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_ISR_STATUS) & 2;
|
return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_ISR) & 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index)
|
static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index)
|
||||||
{
|
{
|
||||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||||
qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_SELECT, index);
|
qpci_io_writeb(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_SEL, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d)
|
static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d)
|
||||||
{
|
{
|
||||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||||
return qpci_io_readw(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_SIZE);
|
return qpci_io_readw(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_NUM);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn)
|
static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn)
|
||||||
{
|
{
|
||||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||||
qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_ADDRESS, pfn);
|
qpci_io_writel(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_PFN, pfn);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d,
|
static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d,
|
||||||
@ -210,9 +213,9 @@ static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d,
|
|||||||
vqpci->vq.size = qvirtio_pci_get_queue_size(d);
|
vqpci->vq.size = qvirtio_pci_get_queue_size(d);
|
||||||
vqpci->vq.free_head = 0;
|
vqpci->vq.free_head = 0;
|
||||||
vqpci->vq.num_free = vqpci->vq.size;
|
vqpci->vq.num_free = vqpci->vq.size;
|
||||||
vqpci->vq.align = QVIRTIO_PCI_ALIGN;
|
vqpci->vq.align = VIRTIO_PCI_VRING_ALIGN;
|
||||||
vqpci->vq.indirect = (feat & QVIRTIO_F_RING_INDIRECT_DESC) != 0;
|
vqpci->vq.indirect = (feat & (1u << VIRTIO_RING_F_INDIRECT_DESC)) != 0;
|
||||||
vqpci->vq.event = (feat & QVIRTIO_F_RING_EVENT_IDX) != 0;
|
vqpci->vq.event = (feat & (1u << VIRTIO_RING_F_EVENT_IDX)) != 0;
|
||||||
|
|
||||||
vqpci->msix_entry = -1;
|
vqpci->msix_entry = -1;
|
||||||
vqpci->msix_addr = 0;
|
vqpci->msix_addr = 0;
|
||||||
@ -224,17 +227,27 @@ static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d,
|
|||||||
/* Check power of 2 */
|
/* Check power of 2 */
|
||||||
g_assert_cmpint(vqpci->vq.size & (vqpci->vq.size - 1), ==, 0);
|
g_assert_cmpint(vqpci->vq.size & (vqpci->vq.size - 1), ==, 0);
|
||||||
|
|
||||||
addr = guest_alloc(alloc, qvring_size(vqpci->vq.size, QVIRTIO_PCI_ALIGN));
|
addr = guest_alloc(alloc, qvring_size(vqpci->vq.size,
|
||||||
|
VIRTIO_PCI_VRING_ALIGN));
|
||||||
qvring_init(alloc, &vqpci->vq, addr);
|
qvring_init(alloc, &vqpci->vq, addr);
|
||||||
qvirtio_pci_set_queue_address(d, vqpci->vq.desc / QVIRTIO_PCI_ALIGN);
|
qvirtio_pci_set_queue_address(d, vqpci->vq.desc / VIRTIO_PCI_VRING_ALIGN);
|
||||||
|
|
||||||
return &vqpci->vq;
|
return &vqpci->vq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qvirtio_pci_virtqueue_cleanup(QVirtQueue *vq,
|
||||||
|
QGuestAllocator *alloc)
|
||||||
|
{
|
||||||
|
QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq);
|
||||||
|
|
||||||
|
guest_free(alloc, vq->desc);
|
||||||
|
g_free(vqpci);
|
||||||
|
}
|
||||||
|
|
||||||
static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
|
static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
|
||||||
{
|
{
|
||||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||||
qpci_io_writew(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_NOTIFY, vq->index);
|
qpci_io_writew(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_NOTIFY, vq->index);
|
||||||
}
|
}
|
||||||
|
|
||||||
const QVirtioBus qvirtio_pci = {
|
const QVirtioBus qvirtio_pci = {
|
||||||
@ -253,6 +266,7 @@ const QVirtioBus qvirtio_pci = {
|
|||||||
.get_queue_size = qvirtio_pci_get_queue_size,
|
.get_queue_size = qvirtio_pci_get_queue_size,
|
||||||
.set_queue_address = qvirtio_pci_set_queue_address,
|
.set_queue_address = qvirtio_pci_set_queue_address,
|
||||||
.virtqueue_setup = qvirtio_pci_virtqueue_setup,
|
.virtqueue_setup = qvirtio_pci_virtqueue_setup,
|
||||||
|
.virtqueue_cleanup = qvirtio_pci_virtqueue_cleanup,
|
||||||
.virtqueue_kick = qvirtio_pci_virtqueue_kick,
|
.virtqueue_kick = qvirtio_pci_virtqueue_kick,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -263,7 +277,7 @@ void qvirtio_pci_foreach(QPCIBus *bus, uint16_t device_type,
|
|||||||
.device_type = device_type,
|
.device_type = device_type,
|
||||||
.user_data = data };
|
.user_data = data };
|
||||||
|
|
||||||
qpci_device_foreach(bus, QVIRTIO_VENDOR_ID, -1,
|
qpci_device_foreach(bus, PCI_VENDOR_ID_REDHAT_QUMRANET, -1,
|
||||||
qvirtio_pci_foreach_callback, &d);
|
qvirtio_pci_foreach_callback, &d);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,9 +328,9 @@ void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci,
|
|||||||
control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
|
control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
|
||||||
|
|
||||||
qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index);
|
qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index);
|
||||||
qpci_io_writew(d->pdev, d->addr + QVIRTIO_PCI_MSIX_QUEUE_VECTOR, entry);
|
qpci_io_writew(d->pdev, d->addr + VIRTIO_MSI_QUEUE_VECTOR, entry);
|
||||||
vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_PCI_MSIX_QUEUE_VECTOR);
|
vector = qpci_io_readw(d->pdev, d->addr + VIRTIO_MSI_QUEUE_VECTOR);
|
||||||
g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR);
|
g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d,
|
void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d,
|
||||||
@ -346,7 +360,7 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d,
|
|||||||
qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL,
|
qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL,
|
||||||
control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
|
control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
|
||||||
|
|
||||||
qpci_io_writew(d->pdev, d->addr + QVIRTIO_PCI_MSIX_CONF_VECTOR, entry);
|
qpci_io_writew(d->pdev, d->addr + VIRTIO_MSI_CONFIG_VECTOR, entry);
|
||||||
vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_PCI_MSIX_CONF_VECTOR);
|
vector = qpci_io_readw(d->pdev, d->addr + VIRTIO_MSI_CONFIG_VECTOR);
|
||||||
g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR);
|
g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
|
||||||
}
|
}
|
||||||
|
@ -13,23 +13,6 @@
|
|||||||
#include "libqos/virtio.h"
|
#include "libqos/virtio.h"
|
||||||
#include "libqos/pci.h"
|
#include "libqos/pci.h"
|
||||||
|
|
||||||
#define QVIRTIO_PCI_DEVICE_FEATURES 0x00
|
|
||||||
#define QVIRTIO_PCI_GUEST_FEATURES 0x04
|
|
||||||
#define QVIRTIO_PCI_QUEUE_ADDRESS 0x08
|
|
||||||
#define QVIRTIO_PCI_QUEUE_SIZE 0x0C
|
|
||||||
#define QVIRTIO_PCI_QUEUE_SELECT 0x0E
|
|
||||||
#define QVIRTIO_PCI_QUEUE_NOTIFY 0x10
|
|
||||||
#define QVIRTIO_PCI_DEVICE_STATUS 0x12
|
|
||||||
#define QVIRTIO_PCI_ISR_STATUS 0x13
|
|
||||||
#define QVIRTIO_PCI_MSIX_CONF_VECTOR 0x14
|
|
||||||
#define QVIRTIO_PCI_MSIX_QUEUE_VECTOR 0x16
|
|
||||||
#define QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX 0x18
|
|
||||||
#define QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX 0x14
|
|
||||||
|
|
||||||
#define QVIRTIO_PCI_ALIGN 4096
|
|
||||||
|
|
||||||
#define QVIRTIO_MSI_NO_VECTOR 0xFFFF
|
|
||||||
|
|
||||||
typedef struct QVirtioPCIDevice {
|
typedef struct QVirtioPCIDevice {
|
||||||
QVirtioDevice vdev;
|
QVirtioDevice vdev;
|
||||||
QPCIDevice *pdev;
|
QPCIDevice *pdev;
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "libqtest.h"
|
#include "libqtest.h"
|
||||||
#include "libqos/virtio.h"
|
#include "libqos/virtio.h"
|
||||||
|
#include "standard-headers/linux/virtio_config.h"
|
||||||
|
#include "standard-headers/linux/virtio_ring.h"
|
||||||
|
|
||||||
uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d,
|
uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d,
|
||||||
uint64_t addr)
|
uint64_t addr)
|
||||||
@ -52,30 +54,36 @@ QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d,
|
|||||||
return bus->virtqueue_setup(d, alloc, index);
|
return bus->virtqueue_setup(d, alloc, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq,
|
||||||
|
QGuestAllocator *alloc)
|
||||||
|
{
|
||||||
|
return bus->virtqueue_cleanup(vq, alloc);
|
||||||
|
}
|
||||||
|
|
||||||
void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d)
|
void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d)
|
||||||
{
|
{
|
||||||
bus->set_status(d, QVIRTIO_RESET);
|
bus->set_status(d, 0);
|
||||||
g_assert_cmphex(bus->get_status(d), ==, QVIRTIO_RESET);
|
g_assert_cmphex(bus->get_status(d), ==, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d)
|
void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d)
|
||||||
{
|
{
|
||||||
bus->set_status(d, bus->get_status(d) | QVIRTIO_ACKNOWLEDGE);
|
bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE);
|
||||||
g_assert_cmphex(bus->get_status(d), ==, QVIRTIO_ACKNOWLEDGE);
|
g_assert_cmphex(bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d)
|
void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d)
|
||||||
{
|
{
|
||||||
bus->set_status(d, bus->get_status(d) | QVIRTIO_DRIVER);
|
bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER);
|
||||||
g_assert_cmphex(bus->get_status(d), ==,
|
g_assert_cmphex(bus->get_status(d), ==,
|
||||||
QVIRTIO_DRIVER | QVIRTIO_ACKNOWLEDGE);
|
VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d)
|
void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d)
|
||||||
{
|
{
|
||||||
bus->set_status(d, bus->get_status(d) | QVIRTIO_DRIVER_OK);
|
bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK);
|
||||||
g_assert_cmphex(bus->get_status(d), ==,
|
g_assert_cmphex(bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK |
|
||||||
QVIRTIO_DRIVER_OK | QVIRTIO_DRIVER | QVIRTIO_ACKNOWLEDGE);
|
VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d,
|
void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d,
|
||||||
@ -133,7 +141,7 @@ void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
vq->desc = addr;
|
vq->desc = addr;
|
||||||
vq->avail = vq->desc + vq->size*sizeof(QVRingDesc);
|
vq->avail = vq->desc + vq->size * sizeof(struct vring_desc);
|
||||||
vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size)
|
vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size)
|
||||||
+ vq->align - 1) & ~(vq->align - 1));
|
+ vq->align - 1) & ~(vq->align - 1));
|
||||||
|
|
||||||
@ -154,7 +162,7 @@ void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr)
|
|||||||
/* vq->used->flags */
|
/* vq->used->flags */
|
||||||
writew(vq->used, 0);
|
writew(vq->used, 0);
|
||||||
/* vq->used->avail_event */
|
/* vq->used->avail_event */
|
||||||
writew(vq->used+2+(sizeof(struct QVRingUsedElem)*vq->size), 0);
|
writew(vq->used + 2 + sizeof(struct vring_used_elem) * vq->size, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVRingIndirectDesc *qvring_indirect_desc_setup(QVirtioDevice *d,
|
QVRingIndirectDesc *qvring_indirect_desc_setup(QVirtioDevice *d,
|
||||||
@ -165,13 +173,13 @@ QVRingIndirectDesc *qvring_indirect_desc_setup(QVirtioDevice *d,
|
|||||||
|
|
||||||
indirect->index = 0;
|
indirect->index = 0;
|
||||||
indirect->elem = elem;
|
indirect->elem = elem;
|
||||||
indirect->desc = guest_alloc(alloc, sizeof(QVRingDesc)*elem);
|
indirect->desc = guest_alloc(alloc, sizeof(struct vring_desc) * elem);
|
||||||
|
|
||||||
for (i = 0; i < elem - 1; ++i) {
|
for (i = 0; i < elem - 1; ++i) {
|
||||||
/* indirect->desc[i].addr */
|
/* indirect->desc[i].addr */
|
||||||
writeq(indirect->desc + (16 * i), 0);
|
writeq(indirect->desc + (16 * i), 0);
|
||||||
/* indirect->desc[i].flags */
|
/* indirect->desc[i].flags */
|
||||||
writew(indirect->desc + (16 * i) + 12, QVRING_DESC_F_NEXT);
|
writew(indirect->desc + (16 * i) + 12, VRING_DESC_F_NEXT);
|
||||||
/* indirect->desc[i].next */
|
/* indirect->desc[i].next */
|
||||||
writew(indirect->desc + (16 * i) + 14, i + 1);
|
writew(indirect->desc + (16 * i) + 14, i + 1);
|
||||||
}
|
}
|
||||||
@ -189,7 +197,7 @@ void qvring_indirect_desc_add(QVRingIndirectDesc *indirect, uint64_t data,
|
|||||||
flags = readw(indirect->desc + (16 * indirect->index) + 12);
|
flags = readw(indirect->desc + (16 * indirect->index) + 12);
|
||||||
|
|
||||||
if (write) {
|
if (write) {
|
||||||
flags |= QVRING_DESC_F_WRITE;
|
flags |= VRING_DESC_F_WRITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* indirect->desc[indirect->index].addr */
|
/* indirect->desc[indirect->index].addr */
|
||||||
@ -209,11 +217,11 @@ uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write,
|
|||||||
vq->num_free--;
|
vq->num_free--;
|
||||||
|
|
||||||
if (write) {
|
if (write) {
|
||||||
flags |= QVRING_DESC_F_WRITE;
|
flags |= VRING_DESC_F_WRITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (next) {
|
if (next) {
|
||||||
flags |= QVRING_DESC_F_NEXT;
|
flags |= VRING_DESC_F_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vq->desc[vq->free_head].addr */
|
/* vq->desc[vq->free_head].addr */
|
||||||
@ -238,9 +246,9 @@ uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect)
|
|||||||
writeq(vq->desc + (16 * vq->free_head), indirect->desc);
|
writeq(vq->desc + (16 * vq->free_head), indirect->desc);
|
||||||
/* vq->desc[vq->free_head].len */
|
/* vq->desc[vq->free_head].len */
|
||||||
writel(vq->desc + (16 * vq->free_head) + 8,
|
writel(vq->desc + (16 * vq->free_head) + 8,
|
||||||
sizeof(QVRingDesc) * indirect->elem);
|
sizeof(struct vring_desc) * indirect->elem);
|
||||||
/* vq->desc[vq->free_head].flags */
|
/* vq->desc[vq->free_head].flags */
|
||||||
writew(vq->desc + (16 * vq->free_head) + 12, QVRING_DESC_F_INDIRECT);
|
writew(vq->desc + (16 * vq->free_head) + 12, VRING_DESC_F_INDIRECT);
|
||||||
|
|
||||||
return vq->free_head++; /* Return and increase, in this order */
|
return vq->free_head++; /* Return and increase, in this order */
|
||||||
}
|
}
|
||||||
@ -263,10 +271,10 @@ void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq,
|
|||||||
/* Must read after idx is updated */
|
/* Must read after idx is updated */
|
||||||
flags = readw(vq->avail);
|
flags = readw(vq->avail);
|
||||||
avail_event = readw(vq->used + 4 +
|
avail_event = readw(vq->used + 4 +
|
||||||
(sizeof(struct QVRingUsedElem) * vq->size));
|
sizeof(struct vring_used_elem) * vq->size);
|
||||||
|
|
||||||
/* < 1 because we add elements to avail queue one by one */
|
/* < 1 because we add elements to avail queue one by one */
|
||||||
if ((flags & QVRING_USED_F_NO_NOTIFY) == 0 &&
|
if ((flags & VRING_USED_F_NO_NOTIFY) == 0 &&
|
||||||
(!vq->event || (uint16_t)(idx-avail_event) < 1)) {
|
(!vq->event || (uint16_t)(idx-avail_event) < 1)) {
|
||||||
bus->virtqueue_kick(d, vq);
|
bus->virtqueue_kick(d, vq);
|
||||||
}
|
}
|
||||||
|
@ -11,78 +11,19 @@
|
|||||||
#define LIBQOS_VIRTIO_H
|
#define LIBQOS_VIRTIO_H
|
||||||
|
|
||||||
#include "libqos/malloc.h"
|
#include "libqos/malloc.h"
|
||||||
|
#include "standard-headers/linux/virtio_ring.h"
|
||||||
|
|
||||||
#define QVIRTIO_VENDOR_ID 0x1AF4
|
|
||||||
|
|
||||||
#define QVIRTIO_RESET 0x0
|
|
||||||
#define QVIRTIO_ACKNOWLEDGE 0x1
|
|
||||||
#define QVIRTIO_DRIVER 0x2
|
|
||||||
#define QVIRTIO_DRIVER_OK 0x4
|
|
||||||
|
|
||||||
#define QVIRTIO_NET_DEVICE_ID 0x1
|
|
||||||
#define QVIRTIO_BLK_DEVICE_ID 0x2
|
|
||||||
#define QVIRTIO_CONSOLE_DEVICE_ID 0x3
|
|
||||||
#define QVIRTIO_RNG_DEVICE_ID 0x4
|
|
||||||
#define QVIRTIO_BALLOON_DEVICE_ID 0x5
|
|
||||||
#define QVIRTIO_RPMSG_DEVICE_ID 0x7
|
|
||||||
#define QVIRTIO_SCSI_DEVICE_ID 0x8
|
|
||||||
#define QVIRTIO_9P_DEVICE_ID 0x9
|
|
||||||
|
|
||||||
#define QVIRTIO_F_NOTIFY_ON_EMPTY 0x01000000
|
|
||||||
#define QVIRTIO_F_ANY_LAYOUT 0x08000000
|
|
||||||
#define QVIRTIO_F_RING_INDIRECT_DESC 0x10000000
|
|
||||||
#define QVIRTIO_F_RING_EVENT_IDX 0x20000000
|
|
||||||
#define QVIRTIO_F_BAD_FEATURE 0x40000000
|
#define QVIRTIO_F_BAD_FEATURE 0x40000000
|
||||||
|
|
||||||
#define QVRING_DESC_F_NEXT 0x1
|
|
||||||
#define QVRING_DESC_F_WRITE 0x2
|
|
||||||
#define QVRING_DESC_F_INDIRECT 0x4
|
|
||||||
|
|
||||||
#define QVIRTIO_F_NOTIFY_ON_EMPTY 0x01000000
|
|
||||||
#define QVIRTIO_F_ANY_LAYOUT 0x08000000
|
|
||||||
#define QVIRTIO_F_RING_INDIRECT_DESC 0x10000000
|
|
||||||
#define QVIRTIO_F_RING_EVENT_IDX 0x20000000
|
|
||||||
#define QVIRTIO_F_BAD_FEATURE 0x40000000
|
|
||||||
|
|
||||||
#define QVRING_AVAIL_F_NO_INTERRUPT 1
|
|
||||||
|
|
||||||
#define QVRING_USED_F_NO_NOTIFY 1
|
|
||||||
|
|
||||||
typedef struct QVirtioDevice {
|
typedef struct QVirtioDevice {
|
||||||
/* Device type */
|
/* Device type */
|
||||||
uint16_t device_type;
|
uint16_t device_type;
|
||||||
} QVirtioDevice;
|
} QVirtioDevice;
|
||||||
|
|
||||||
typedef struct QVRingDesc {
|
|
||||||
uint64_t addr;
|
|
||||||
uint32_t len;
|
|
||||||
uint16_t flags;
|
|
||||||
uint16_t next;
|
|
||||||
} QVRingDesc;
|
|
||||||
|
|
||||||
typedef struct QVRingAvail {
|
|
||||||
uint16_t flags;
|
|
||||||
uint16_t idx;
|
|
||||||
uint16_t ring[0]; /* This is an array of uint16_t */
|
|
||||||
uint16_t used_event;
|
|
||||||
} QVRingAvail;
|
|
||||||
|
|
||||||
typedef struct QVRingUsedElem {
|
|
||||||
uint32_t id;
|
|
||||||
uint32_t len;
|
|
||||||
} QVRingUsedElem;
|
|
||||||
|
|
||||||
typedef struct QVRingUsed {
|
|
||||||
uint16_t flags;
|
|
||||||
uint16_t idx;
|
|
||||||
QVRingUsedElem ring[0]; /* This is an array of QVRingUsedElem structs */
|
|
||||||
uint16_t avail_event;
|
|
||||||
} QVRingUsed;
|
|
||||||
|
|
||||||
typedef struct QVirtQueue {
|
typedef struct QVirtQueue {
|
||||||
uint64_t desc; /* This points to an array of QVRingDesc */
|
uint64_t desc; /* This points to an array of struct vring_desc */
|
||||||
uint64_t avail; /* This points to a QVRingAvail */
|
uint64_t avail; /* This points to a struct vring_avail */
|
||||||
uint64_t used; /* This points to a QVRingDesc */
|
uint64_t used; /* This points to a struct vring_desc */
|
||||||
uint16_t index;
|
uint16_t index;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t free_head;
|
uint32_t free_head;
|
||||||
@ -93,7 +34,7 @@ typedef struct QVirtQueue {
|
|||||||
} QVirtQueue;
|
} QVirtQueue;
|
||||||
|
|
||||||
typedef struct QVRingIndirectDesc {
|
typedef struct QVRingIndirectDesc {
|
||||||
uint64_t desc; /* This points to an array fo QVRingDesc */
|
uint64_t desc; /* This points to an array fo struct vring_desc */
|
||||||
uint16_t index;
|
uint16_t index;
|
||||||
uint16_t elem;
|
uint16_t elem;
|
||||||
} QVRingIndirectDesc;
|
} QVRingIndirectDesc;
|
||||||
@ -138,15 +79,18 @@ typedef struct QVirtioBus {
|
|||||||
QVirtQueue *(*virtqueue_setup)(QVirtioDevice *d, QGuestAllocator *alloc,
|
QVirtQueue *(*virtqueue_setup)(QVirtioDevice *d, QGuestAllocator *alloc,
|
||||||
uint16_t index);
|
uint16_t index);
|
||||||
|
|
||||||
|
/* Free virtqueue resources */
|
||||||
|
void (*virtqueue_cleanup)(QVirtQueue *vq, QGuestAllocator *alloc);
|
||||||
|
|
||||||
/* Notify changes in virtqueue */
|
/* Notify changes in virtqueue */
|
||||||
void (*virtqueue_kick)(QVirtioDevice *d, QVirtQueue *vq);
|
void (*virtqueue_kick)(QVirtioDevice *d, QVirtQueue *vq);
|
||||||
} QVirtioBus;
|
} QVirtioBus;
|
||||||
|
|
||||||
static inline uint32_t qvring_size(uint32_t num, uint32_t align)
|
static inline uint32_t qvring_size(uint32_t num, uint32_t align)
|
||||||
{
|
{
|
||||||
return ((sizeof(struct QVRingDesc) * num + sizeof(uint16_t) * (3 + num)
|
return ((sizeof(struct vring_desc) * num + sizeof(uint16_t) * (3 + num)
|
||||||
+ align - 1) & ~(align - 1))
|
+ align - 1) & ~(align - 1))
|
||||||
+ sizeof(uint16_t) * 3 + sizeof(struct QVRingUsedElem) * num;
|
+ sizeof(uint16_t) * 3 + sizeof(struct vring_used_elem) * num;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d,
|
uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d,
|
||||||
@ -177,6 +121,8 @@ void qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d,
|
|||||||
gint64 timeout_us);
|
gint64 timeout_us);
|
||||||
QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d,
|
QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d,
|
||||||
QGuestAllocator *alloc, uint16_t index);
|
QGuestAllocator *alloc, uint16_t index);
|
||||||
|
void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq,
|
||||||
|
QGuestAllocator *alloc);
|
||||||
|
|
||||||
void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr);
|
void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr);
|
||||||
QVRingIndirectDesc *qvring_indirect_desc_setup(QVirtioDevice *d,
|
QVRingIndirectDesc *qvring_indirect_desc_setup(QVirtioDevice *d,
|
||||||
|
@ -18,25 +18,11 @@
|
|||||||
#include "libqos/malloc-pc.h"
|
#include "libqos/malloc-pc.h"
|
||||||
#include "libqos/malloc-generic.h"
|
#include "libqos/malloc-generic.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
|
#include "standard-headers/linux/virtio_ids.h"
|
||||||
#define QVIRTIO_BLK_F_BARRIER 0x00000001
|
#include "standard-headers/linux/virtio_config.h"
|
||||||
#define QVIRTIO_BLK_F_SIZE_MAX 0x00000002
|
#include "standard-headers/linux/virtio_ring.h"
|
||||||
#define QVIRTIO_BLK_F_SEG_MAX 0x00000004
|
#include "standard-headers/linux/virtio_blk.h"
|
||||||
#define QVIRTIO_BLK_F_GEOMETRY 0x00000010
|
#include "standard-headers/linux/virtio_pci.h"
|
||||||
#define QVIRTIO_BLK_F_RO 0x00000020
|
|
||||||
#define QVIRTIO_BLK_F_BLK_SIZE 0x00000040
|
|
||||||
#define QVIRTIO_BLK_F_SCSI 0x00000080
|
|
||||||
#define QVIRTIO_BLK_F_WCE 0x00000200
|
|
||||||
#define QVIRTIO_BLK_F_TOPOLOGY 0x00000400
|
|
||||||
#define QVIRTIO_BLK_F_CONFIG_WCE 0x00000800
|
|
||||||
|
|
||||||
#define QVIRTIO_BLK_T_IN 0
|
|
||||||
#define QVIRTIO_BLK_T_OUT 1
|
|
||||||
#define QVIRTIO_BLK_T_SCSI_CMD 2
|
|
||||||
#define QVIRTIO_BLK_T_SCSI_CMD_OUT 3
|
|
||||||
#define QVIRTIO_BLK_T_FLUSH 4
|
|
||||||
#define QVIRTIO_BLK_T_FLUSH_OUT 5
|
|
||||||
#define QVIRTIO_BLK_T_GET_ID 8
|
|
||||||
|
|
||||||
#define TEST_IMAGE_SIZE (64 * 1024 * 1024)
|
#define TEST_IMAGE_SIZE (64 * 1024 * 1024)
|
||||||
#define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000)
|
#define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000)
|
||||||
@ -118,9 +104,9 @@ static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot)
|
|||||||
{
|
{
|
||||||
QVirtioPCIDevice *dev;
|
QVirtioPCIDevice *dev;
|
||||||
|
|
||||||
dev = qvirtio_pci_device_find(bus, QVIRTIO_BLK_DEVICE_ID);
|
dev = qvirtio_pci_device_find(bus, VIRTIO_ID_BLOCK);
|
||||||
g_assert(dev != NULL);
|
g_assert(dev != NULL);
|
||||||
g_assert_cmphex(dev->vdev.device_type, ==, QVIRTIO_BLK_DEVICE_ID);
|
g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK);
|
||||||
g_assert_cmphex(dev->pdev->devfn, ==, ((slot << 3) | PCI_FN));
|
g_assert_cmphex(dev->pdev->devfn, ==, ((slot << 3) | PCI_FN));
|
||||||
|
|
||||||
qvirtio_pci_device_enable(dev);
|
qvirtio_pci_device_enable(dev);
|
||||||
@ -181,15 +167,16 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev,
|
|||||||
|
|
||||||
features = qvirtio_get_features(bus, dev);
|
features = qvirtio_get_features(bus, dev);
|
||||||
features = features & ~(QVIRTIO_F_BAD_FEATURE |
|
features = features & ~(QVIRTIO_F_BAD_FEATURE |
|
||||||
QVIRTIO_F_RING_INDIRECT_DESC | QVIRTIO_F_RING_EVENT_IDX |
|
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
|
||||||
QVIRTIO_BLK_F_SCSI);
|
(1u << VIRTIO_RING_F_EVENT_IDX) |
|
||||||
|
(1u << VIRTIO_BLK_F_SCSI));
|
||||||
qvirtio_set_features(bus, dev, features);
|
qvirtio_set_features(bus, dev, features);
|
||||||
|
|
||||||
qvirtio_set_driver_ok(bus, dev);
|
qvirtio_set_driver_ok(bus, dev);
|
||||||
|
|
||||||
/* Write and read with 3 descriptor layout */
|
/* Write and read with 3 descriptor layout */
|
||||||
/* Write request */
|
/* Write request */
|
||||||
req.type = QVIRTIO_BLK_T_OUT;
|
req.type = VIRTIO_BLK_T_OUT;
|
||||||
req.ioprio = 1;
|
req.ioprio = 1;
|
||||||
req.sector = 0;
|
req.sector = 0;
|
||||||
req.data = g_malloc0(512);
|
req.data = g_malloc0(512);
|
||||||
@ -212,7 +199,7 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev,
|
|||||||
guest_free(alloc, req_addr);
|
guest_free(alloc, req_addr);
|
||||||
|
|
||||||
/* Read request */
|
/* Read request */
|
||||||
req.type = QVIRTIO_BLK_T_IN;
|
req.type = VIRTIO_BLK_T_IN;
|
||||||
req.ioprio = 1;
|
req.ioprio = 1;
|
||||||
req.sector = 0;
|
req.sector = 0;
|
||||||
req.data = g_malloc0(512);
|
req.data = g_malloc0(512);
|
||||||
@ -238,10 +225,10 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev,
|
|||||||
|
|
||||||
guest_free(alloc, req_addr);
|
guest_free(alloc, req_addr);
|
||||||
|
|
||||||
if (features & QVIRTIO_F_ANY_LAYOUT) {
|
if (features & (1u << VIRTIO_F_ANY_LAYOUT)) {
|
||||||
/* Write and read with 2 descriptor layout */
|
/* Write and read with 2 descriptor layout */
|
||||||
/* Write request */
|
/* Write request */
|
||||||
req.type = QVIRTIO_BLK_T_OUT;
|
req.type = VIRTIO_BLK_T_OUT;
|
||||||
req.ioprio = 1;
|
req.ioprio = 1;
|
||||||
req.sector = 1;
|
req.sector = 1;
|
||||||
req.data = g_malloc0(512);
|
req.data = g_malloc0(512);
|
||||||
@ -262,7 +249,7 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev,
|
|||||||
guest_free(alloc, req_addr);
|
guest_free(alloc, req_addr);
|
||||||
|
|
||||||
/* Read request */
|
/* Read request */
|
||||||
req.type = QVIRTIO_BLK_T_IN;
|
req.type = VIRTIO_BLK_T_IN;
|
||||||
req.ioprio = 1;
|
req.ioprio = 1;
|
||||||
req.sector = 1;
|
req.sector = 1;
|
||||||
req.data = g_malloc0(512);
|
req.data = g_malloc0(512);
|
||||||
@ -305,13 +292,13 @@ static void pci_basic(void)
|
|||||||
alloc, 0);
|
alloc, 0);
|
||||||
|
|
||||||
/* MSI-X is not enabled */
|
/* MSI-X is not enabled */
|
||||||
addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX;
|
addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false);
|
||||||
|
|
||||||
test_basic(&qvirtio_pci, &dev->vdev, alloc, &vqpci->vq,
|
test_basic(&qvirtio_pci, &dev->vdev, alloc, &vqpci->vq,
|
||||||
(uint64_t)(uintptr_t)addr);
|
(uint64_t)(uintptr_t)addr);
|
||||||
|
|
||||||
/* End test */
|
/* End test */
|
||||||
guest_free(alloc, vqpci->vq.desc);
|
qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc);
|
||||||
pc_alloc_uninit(alloc);
|
pc_alloc_uninit(alloc);
|
||||||
qvirtio_pci_device_disable(dev);
|
qvirtio_pci_device_disable(dev);
|
||||||
g_free(dev);
|
g_free(dev);
|
||||||
@ -340,16 +327,17 @@ static void pci_indirect(void)
|
|||||||
dev = virtio_blk_pci_init(bus, PCI_SLOT);
|
dev = virtio_blk_pci_init(bus, PCI_SLOT);
|
||||||
|
|
||||||
/* MSI-X is not enabled */
|
/* MSI-X is not enabled */
|
||||||
addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX;
|
addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false);
|
||||||
|
|
||||||
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
|
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
|
||||||
(uint64_t)(uintptr_t)addr);
|
(uint64_t)(uintptr_t)addr);
|
||||||
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
|
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
|
||||||
|
|
||||||
features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
|
features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
|
||||||
g_assert_cmphex(features & QVIRTIO_F_RING_INDIRECT_DESC, !=, 0);
|
g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0);
|
||||||
features = features & ~(QVIRTIO_F_BAD_FEATURE | QVIRTIO_F_RING_EVENT_IDX |
|
features = features & ~(QVIRTIO_F_BAD_FEATURE |
|
||||||
QVIRTIO_BLK_F_SCSI);
|
(1u << VIRTIO_RING_F_EVENT_IDX) |
|
||||||
|
(1u << VIRTIO_BLK_F_SCSI));
|
||||||
qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
|
qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
|
||||||
|
|
||||||
alloc = pc_alloc_init();
|
alloc = pc_alloc_init();
|
||||||
@ -358,7 +346,7 @@ static void pci_indirect(void)
|
|||||||
qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
|
qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
|
||||||
|
|
||||||
/* Write request */
|
/* Write request */
|
||||||
req.type = QVIRTIO_BLK_T_OUT;
|
req.type = VIRTIO_BLK_T_OUT;
|
||||||
req.ioprio = 1;
|
req.ioprio = 1;
|
||||||
req.sector = 0;
|
req.sector = 0;
|
||||||
req.data = g_malloc0(512);
|
req.data = g_malloc0(512);
|
||||||
@ -383,7 +371,7 @@ static void pci_indirect(void)
|
|||||||
guest_free(alloc, req_addr);
|
guest_free(alloc, req_addr);
|
||||||
|
|
||||||
/* Read request */
|
/* Read request */
|
||||||
req.type = QVIRTIO_BLK_T_IN;
|
req.type = VIRTIO_BLK_T_IN;
|
||||||
req.ioprio = 1;
|
req.ioprio = 1;
|
||||||
req.sector = 0;
|
req.sector = 0;
|
||||||
req.data = g_malloc0(512);
|
req.data = g_malloc0(512);
|
||||||
@ -413,7 +401,7 @@ static void pci_indirect(void)
|
|||||||
guest_free(alloc, req_addr);
|
guest_free(alloc, req_addr);
|
||||||
|
|
||||||
/* End test */
|
/* End test */
|
||||||
guest_free(alloc, vqpci->vq.desc);
|
qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc);
|
||||||
pc_alloc_uninit(alloc);
|
pc_alloc_uninit(alloc);
|
||||||
qvirtio_pci_device_disable(dev);
|
qvirtio_pci_device_disable(dev);
|
||||||
g_free(dev);
|
g_free(dev);
|
||||||
@ -434,7 +422,7 @@ static void pci_config(void)
|
|||||||
dev = virtio_blk_pci_init(bus, PCI_SLOT);
|
dev = virtio_blk_pci_init(bus, PCI_SLOT);
|
||||||
|
|
||||||
/* MSI-X is not enabled */
|
/* MSI-X is not enabled */
|
||||||
addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX;
|
addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false);
|
||||||
|
|
||||||
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
|
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
|
||||||
(uint64_t)(uintptr_t)addr);
|
(uint64_t)(uintptr_t)addr);
|
||||||
@ -481,7 +469,7 @@ static void pci_msix(void)
|
|||||||
qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0);
|
qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0);
|
||||||
|
|
||||||
/* MSI-X is enabled */
|
/* MSI-X is enabled */
|
||||||
addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX;
|
addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(true);
|
||||||
|
|
||||||
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
|
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
|
||||||
(uint64_t)(uintptr_t)addr);
|
(uint64_t)(uintptr_t)addr);
|
||||||
@ -489,8 +477,9 @@ static void pci_msix(void)
|
|||||||
|
|
||||||
features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
|
features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
|
||||||
features = features & ~(QVIRTIO_F_BAD_FEATURE |
|
features = features & ~(QVIRTIO_F_BAD_FEATURE |
|
||||||
QVIRTIO_F_RING_INDIRECT_DESC |
|
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
|
||||||
QVIRTIO_F_RING_EVENT_IDX | QVIRTIO_BLK_F_SCSI);
|
(1u << VIRTIO_RING_F_EVENT_IDX) |
|
||||||
|
(1u << VIRTIO_BLK_F_SCSI));
|
||||||
qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
|
qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
|
||||||
|
|
||||||
vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
|
vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
|
||||||
@ -509,7 +498,7 @@ static void pci_msix(void)
|
|||||||
g_assert_cmpint(capacity, ==, n_size / 512);
|
g_assert_cmpint(capacity, ==, n_size / 512);
|
||||||
|
|
||||||
/* Write request */
|
/* Write request */
|
||||||
req.type = QVIRTIO_BLK_T_OUT;
|
req.type = VIRTIO_BLK_T_OUT;
|
||||||
req.ioprio = 1;
|
req.ioprio = 1;
|
||||||
req.sector = 0;
|
req.sector = 0;
|
||||||
req.data = g_malloc0(512);
|
req.data = g_malloc0(512);
|
||||||
@ -533,7 +522,7 @@ static void pci_msix(void)
|
|||||||
guest_free(alloc, req_addr);
|
guest_free(alloc, req_addr);
|
||||||
|
|
||||||
/* Read request */
|
/* Read request */
|
||||||
req.type = QVIRTIO_BLK_T_IN;
|
req.type = VIRTIO_BLK_T_IN;
|
||||||
req.ioprio = 1;
|
req.ioprio = 1;
|
||||||
req.sector = 0;
|
req.sector = 0;
|
||||||
req.data = g_malloc0(512);
|
req.data = g_malloc0(512);
|
||||||
@ -563,7 +552,7 @@ static void pci_msix(void)
|
|||||||
guest_free(alloc, req_addr);
|
guest_free(alloc, req_addr);
|
||||||
|
|
||||||
/* End test */
|
/* End test */
|
||||||
guest_free(alloc, vqpci->vq.desc);
|
qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc);
|
||||||
pc_alloc_uninit(alloc);
|
pc_alloc_uninit(alloc);
|
||||||
qpci_msix_disable(dev->pdev);
|
qpci_msix_disable(dev->pdev);
|
||||||
qvirtio_pci_device_disable(dev);
|
qvirtio_pci_device_disable(dev);
|
||||||
@ -596,7 +585,7 @@ static void pci_idx(void)
|
|||||||
qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0);
|
qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0);
|
||||||
|
|
||||||
/* MSI-X is enabled */
|
/* MSI-X is enabled */
|
||||||
addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX;
|
addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(true);
|
||||||
|
|
||||||
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
|
capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
|
||||||
(uint64_t)(uintptr_t)addr);
|
(uint64_t)(uintptr_t)addr);
|
||||||
@ -604,8 +593,9 @@ static void pci_idx(void)
|
|||||||
|
|
||||||
features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
|
features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
|
||||||
features = features & ~(QVIRTIO_F_BAD_FEATURE |
|
features = features & ~(QVIRTIO_F_BAD_FEATURE |
|
||||||
QVIRTIO_F_RING_INDIRECT_DESC |
|
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
|
||||||
QVIRTIO_F_NOTIFY_ON_EMPTY | QVIRTIO_BLK_F_SCSI);
|
(1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
|
||||||
|
(1u << VIRTIO_BLK_F_SCSI));
|
||||||
qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
|
qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
|
||||||
|
|
||||||
vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
|
vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
|
||||||
@ -615,7 +605,7 @@ static void pci_idx(void)
|
|||||||
qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
|
qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
|
||||||
|
|
||||||
/* Write request */
|
/* Write request */
|
||||||
req.type = QVIRTIO_BLK_T_OUT;
|
req.type = VIRTIO_BLK_T_OUT;
|
||||||
req.ioprio = 1;
|
req.ioprio = 1;
|
||||||
req.sector = 0;
|
req.sector = 0;
|
||||||
req.data = g_malloc0(512);
|
req.data = g_malloc0(512);
|
||||||
@ -634,7 +624,7 @@ static void pci_idx(void)
|
|||||||
QVIRTIO_BLK_TIMEOUT_US);
|
QVIRTIO_BLK_TIMEOUT_US);
|
||||||
|
|
||||||
/* Write request */
|
/* Write request */
|
||||||
req.type = QVIRTIO_BLK_T_OUT;
|
req.type = VIRTIO_BLK_T_OUT;
|
||||||
req.ioprio = 1;
|
req.ioprio = 1;
|
||||||
req.sector = 1;
|
req.sector = 1;
|
||||||
req.data = g_malloc0(512);
|
req.data = g_malloc0(512);
|
||||||
@ -660,7 +650,7 @@ static void pci_idx(void)
|
|||||||
guest_free(alloc, req_addr);
|
guest_free(alloc, req_addr);
|
||||||
|
|
||||||
/* Read request */
|
/* Read request */
|
||||||
req.type = QVIRTIO_BLK_T_IN;
|
req.type = VIRTIO_BLK_T_IN;
|
||||||
req.ioprio = 1;
|
req.ioprio = 1;
|
||||||
req.sector = 1;
|
req.sector = 1;
|
||||||
req.data = g_malloc0(512);
|
req.data = g_malloc0(512);
|
||||||
@ -689,7 +679,7 @@ static void pci_idx(void)
|
|||||||
guest_free(alloc, req_addr);
|
guest_free(alloc, req_addr);
|
||||||
|
|
||||||
/* End test */
|
/* End test */
|
||||||
guest_free(alloc, vqpci->vq.desc);
|
qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc);
|
||||||
pc_alloc_uninit(alloc);
|
pc_alloc_uninit(alloc);
|
||||||
qpci_msix_disable(dev->pdev);
|
qpci_msix_disable(dev->pdev);
|
||||||
qvirtio_pci_device_disable(dev);
|
qvirtio_pci_device_disable(dev);
|
||||||
@ -732,7 +722,7 @@ static void mmio_basic(void)
|
|||||||
|
|
||||||
dev = qvirtio_mmio_init_device(MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE);
|
dev = qvirtio_mmio_init_device(MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE);
|
||||||
g_assert(dev != NULL);
|
g_assert(dev != NULL);
|
||||||
g_assert_cmphex(dev->vdev.device_type, ==, QVIRTIO_BLK_DEVICE_ID);
|
g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK);
|
||||||
|
|
||||||
qvirtio_reset(&qvirtio_mmio, &dev->vdev);
|
qvirtio_reset(&qvirtio_mmio, &dev->vdev);
|
||||||
qvirtio_set_acknowledge(&qvirtio_mmio, &dev->vdev);
|
qvirtio_set_acknowledge(&qvirtio_mmio, &dev->vdev);
|
||||||
@ -755,7 +745,7 @@ static void mmio_basic(void)
|
|||||||
g_assert_cmpint(capacity, ==, n_size / 512);
|
g_assert_cmpint(capacity, ==, n_size / 512);
|
||||||
|
|
||||||
/* End test */
|
/* End test */
|
||||||
guest_free(alloc, vq->desc);
|
qvirtqueue_cleanup(&qvirtio_mmio, vq, alloc);
|
||||||
generic_alloc_uninit(alloc);
|
generic_alloc_uninit(alloc);
|
||||||
g_free(dev);
|
g_free(dev);
|
||||||
test_end();
|
test_end();
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
#include "libqos/malloc-generic.h"
|
#include "libqos/malloc-generic.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
#include "hw/virtio/virtio-net.h"
|
#include "hw/virtio/virtio-net.h"
|
||||||
|
#include "standard-headers/linux/virtio_ids.h"
|
||||||
|
#include "standard-headers/linux/virtio_ring.h"
|
||||||
|
|
||||||
#define PCI_SLOT_HP 0x06
|
#define PCI_SLOT_HP 0x06
|
||||||
#define PCI_SLOT 0x04
|
#define PCI_SLOT 0x04
|
||||||
@ -39,9 +41,9 @@ static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot)
|
|||||||
{
|
{
|
||||||
QVirtioPCIDevice *dev;
|
QVirtioPCIDevice *dev;
|
||||||
|
|
||||||
dev = qvirtio_pci_device_find(bus, QVIRTIO_NET_DEVICE_ID);
|
dev = qvirtio_pci_device_find(bus, VIRTIO_ID_NET);
|
||||||
g_assert(dev != NULL);
|
g_assert(dev != NULL);
|
||||||
g_assert_cmphex(dev->vdev.device_type, ==, QVIRTIO_NET_DEVICE_ID);
|
g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_NET);
|
||||||
|
|
||||||
qvirtio_pci_device_enable(dev);
|
qvirtio_pci_device_enable(dev);
|
||||||
qvirtio_reset(&qvirtio_pci, &dev->vdev);
|
qvirtio_reset(&qvirtio_pci, &dev->vdev);
|
||||||
@ -69,8 +71,8 @@ static void driver_init(const QVirtioBus *bus, QVirtioDevice *dev)
|
|||||||
|
|
||||||
features = qvirtio_get_features(bus, dev);
|
features = qvirtio_get_features(bus, dev);
|
||||||
features = features & ~(QVIRTIO_F_BAD_FEATURE |
|
features = features & ~(QVIRTIO_F_BAD_FEATURE |
|
||||||
QVIRTIO_F_RING_INDIRECT_DESC |
|
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
|
||||||
QVIRTIO_F_RING_EVENT_IDX);
|
(1u << VIRTIO_RING_F_EVENT_IDX));
|
||||||
qvirtio_set_features(bus, dev, features);
|
qvirtio_set_features(bus, dev, features);
|
||||||
|
|
||||||
qvirtio_set_driver_ok(bus, dev);
|
qvirtio_set_driver_ok(bus, dev);
|
||||||
@ -227,7 +229,7 @@ static void pci_basic(gconstpointer data)
|
|||||||
|
|
||||||
/* End test */
|
/* End test */
|
||||||
close(sv[0]);
|
close(sv[0]);
|
||||||
guest_free(alloc, tx->vq.desc);
|
qvirtqueue_cleanup(&qvirtio_pci, &tx->vq, alloc);
|
||||||
pc_alloc_uninit(alloc);
|
pc_alloc_uninit(alloc);
|
||||||
qvirtio_pci_device_disable(dev);
|
qvirtio_pci_device_disable(dev);
|
||||||
g_free(dev);
|
g_free(dev);
|
||||||
|
@ -17,11 +17,13 @@
|
|||||||
#include "libqos/malloc.h"
|
#include "libqos/malloc.h"
|
||||||
#include "libqos/malloc-pc.h"
|
#include "libqos/malloc-pc.h"
|
||||||
#include "libqos/malloc-generic.h"
|
#include "libqos/malloc-generic.h"
|
||||||
|
#include "standard-headers/linux/virtio_ids.h"
|
||||||
|
#include "standard-headers/linux/virtio_pci.h"
|
||||||
|
#include "standard-headers/linux/virtio_scsi.h"
|
||||||
|
|
||||||
#define PCI_SLOT 0x02
|
#define PCI_SLOT 0x02
|
||||||
#define PCI_FN 0x00
|
#define PCI_FN 0x00
|
||||||
#define QVIRTIO_SCSI_TIMEOUT_US (1 * 1000 * 1000)
|
#define QVIRTIO_SCSI_TIMEOUT_US (1 * 1000 * 1000)
|
||||||
#define CDB_SIZE 32
|
|
||||||
|
|
||||||
#define MAX_NUM_QUEUES 64
|
#define MAX_NUM_QUEUES 64
|
||||||
|
|
||||||
@ -33,24 +35,6 @@ typedef struct {
|
|||||||
QVirtQueue *vq[MAX_NUM_QUEUES + 2];
|
QVirtQueue *vq[MAX_NUM_QUEUES + 2];
|
||||||
} QVirtIOSCSI;
|
} QVirtIOSCSI;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t lun[8];
|
|
||||||
int64_t tag;
|
|
||||||
uint8_t task_attr;
|
|
||||||
uint8_t prio;
|
|
||||||
uint8_t crn;
|
|
||||||
uint8_t cdb[CDB_SIZE];
|
|
||||||
} QEMU_PACKED QVirtIOSCSICmdReq;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t sense_len;
|
|
||||||
uint32_t resid;
|
|
||||||
uint16_t status_qualifier;
|
|
||||||
uint8_t status;
|
|
||||||
uint8_t response;
|
|
||||||
uint8_t sense[96];
|
|
||||||
} QEMU_PACKED QVirtIOSCSICmdResp;
|
|
||||||
|
|
||||||
static void qvirtio_scsi_start(const char *extra_opts)
|
static void qvirtio_scsi_start(const char *extra_opts)
|
||||||
{
|
{
|
||||||
char *cmdline;
|
char *cmdline;
|
||||||
@ -74,7 +58,7 @@ static void qvirtio_scsi_pci_free(QVirtIOSCSI *vs)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < vs->num_queues + 2; i++) {
|
for (i = 0; i < vs->num_queues + 2; i++) {
|
||||||
guest_free(vs->alloc, vs->vq[i]->desc);
|
qvirtqueue_cleanup(&qvirtio_pci, vs->vq[i], vs->alloc);
|
||||||
}
|
}
|
||||||
pc_alloc_uninit(vs->alloc);
|
pc_alloc_uninit(vs->alloc);
|
||||||
qvirtio_pci_device_disable(container_of(vs->dev, QVirtioPCIDevice, vdev));
|
qvirtio_pci_device_disable(container_of(vs->dev, QVirtioPCIDevice, vdev));
|
||||||
@ -99,11 +83,11 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb,
|
|||||||
const uint8_t *data_in,
|
const uint8_t *data_in,
|
||||||
size_t data_in_len,
|
size_t data_in_len,
|
||||||
uint8_t *data_out, size_t data_out_len,
|
uint8_t *data_out, size_t data_out_len,
|
||||||
QVirtIOSCSICmdResp *resp_out)
|
struct virtio_scsi_cmd_resp *resp_out)
|
||||||
{
|
{
|
||||||
QVirtQueue *vq;
|
QVirtQueue *vq;
|
||||||
QVirtIOSCSICmdReq req = { { 0 } };
|
struct virtio_scsi_cmd_req req = { { 0 } };
|
||||||
QVirtIOSCSICmdResp resp = { .response = 0xff, .status = 0xff };
|
struct virtio_scsi_cmd_resp resp = { .response = 0xff, .status = 0xff };
|
||||||
uint64_t req_addr, resp_addr, data_in_addr = 0, data_out_addr = 0;
|
uint64_t req_addr, resp_addr, data_in_addr = 0, data_out_addr = 0;
|
||||||
uint8_t response;
|
uint8_t response;
|
||||||
uint32_t free_head;
|
uint32_t free_head;
|
||||||
@ -112,7 +96,7 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb,
|
|||||||
|
|
||||||
req.lun[0] = 1; /* Select LUN */
|
req.lun[0] = 1; /* Select LUN */
|
||||||
req.lun[1] = 1; /* Select target 1 */
|
req.lun[1] = 1; /* Select target 1 */
|
||||||
memcpy(req.cdb, cdb, CDB_SIZE);
|
memcpy(req.cdb, cdb, VIRTIO_SCSI_CDB_SIZE);
|
||||||
|
|
||||||
/* XXX: Fix endian if any multi-byte field in req/resp is used */
|
/* XXX: Fix endian if any multi-byte field in req/resp is used */
|
||||||
|
|
||||||
@ -137,7 +121,8 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb,
|
|||||||
qvirtqueue_kick(&qvirtio_pci, vs->dev, vq, free_head);
|
qvirtqueue_kick(&qvirtio_pci, vs->dev, vq, free_head);
|
||||||
qvirtio_wait_queue_isr(&qvirtio_pci, vs->dev, vq, QVIRTIO_SCSI_TIMEOUT_US);
|
qvirtio_wait_queue_isr(&qvirtio_pci, vs->dev, vq, QVIRTIO_SCSI_TIMEOUT_US);
|
||||||
|
|
||||||
response = readb(resp_addr + offsetof(QVirtIOSCSICmdResp, response));
|
response = readb(resp_addr +
|
||||||
|
offsetof(struct virtio_scsi_cmd_resp, response));
|
||||||
|
|
||||||
if (resp_out) {
|
if (resp_out) {
|
||||||
memread(resp_addr, resp_out, sizeof(*resp_out));
|
memread(resp_addr, resp_out, sizeof(*resp_out));
|
||||||
@ -152,10 +137,10 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb,
|
|||||||
|
|
||||||
static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot)
|
static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot)
|
||||||
{
|
{
|
||||||
const uint8_t test_unit_ready_cdb[CDB_SIZE] = {};
|
const uint8_t test_unit_ready_cdb[VIRTIO_SCSI_CDB_SIZE] = {};
|
||||||
QVirtIOSCSI *vs;
|
QVirtIOSCSI *vs;
|
||||||
QVirtioPCIDevice *dev;
|
QVirtioPCIDevice *dev;
|
||||||
QVirtIOSCSICmdResp resp;
|
struct virtio_scsi_cmd_resp resp;
|
||||||
void *addr;
|
void *addr;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -163,17 +148,17 @@ static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot)
|
|||||||
vs->alloc = pc_alloc_init();
|
vs->alloc = pc_alloc_init();
|
||||||
vs->bus = qpci_init_pc();
|
vs->bus = qpci_init_pc();
|
||||||
|
|
||||||
dev = qvirtio_pci_device_find(vs->bus, QVIRTIO_SCSI_DEVICE_ID);
|
dev = qvirtio_pci_device_find(vs->bus, VIRTIO_ID_SCSI);
|
||||||
vs->dev = (QVirtioDevice *)dev;
|
vs->dev = (QVirtioDevice *)dev;
|
||||||
g_assert(dev != NULL);
|
g_assert(dev != NULL);
|
||||||
g_assert_cmphex(vs->dev->device_type, ==, QVIRTIO_SCSI_DEVICE_ID);
|
g_assert_cmphex(vs->dev->device_type, ==, VIRTIO_ID_SCSI);
|
||||||
|
|
||||||
qvirtio_pci_device_enable(dev);
|
qvirtio_pci_device_enable(dev);
|
||||||
qvirtio_reset(&qvirtio_pci, vs->dev);
|
qvirtio_reset(&qvirtio_pci, vs->dev);
|
||||||
qvirtio_set_acknowledge(&qvirtio_pci, vs->dev);
|
qvirtio_set_acknowledge(&qvirtio_pci, vs->dev);
|
||||||
qvirtio_set_driver(&qvirtio_pci, vs->dev);
|
qvirtio_set_driver(&qvirtio_pci, vs->dev);
|
||||||
|
|
||||||
addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX;
|
addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false);
|
||||||
vs->num_queues = qvirtio_config_readl(&qvirtio_pci, vs->dev,
|
vs->num_queues = qvirtio_config_readl(&qvirtio_pci, vs->dev,
|
||||||
(uint64_t)(uintptr_t)addr);
|
(uint64_t)(uintptr_t)addr);
|
||||||
|
|
||||||
@ -238,10 +223,12 @@ static void test_unaligned_write_same(void)
|
|||||||
QVirtIOSCSI *vs;
|
QVirtIOSCSI *vs;
|
||||||
uint8_t buf1[512] = { 0 };
|
uint8_t buf1[512] = { 0 };
|
||||||
uint8_t buf2[512] = { 1 };
|
uint8_t buf2[512] = { 1 };
|
||||||
const uint8_t write_same_cdb_1[CDB_SIZE] = { 0x41, 0x00, 0x00, 0x00, 0x00,
|
const uint8_t write_same_cdb_1[VIRTIO_SCSI_CDB_SIZE] = {
|
||||||
0x01, 0x00, 0x00, 0x02, 0x00 };
|
0x41, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00
|
||||||
const uint8_t write_same_cdb_2[CDB_SIZE] = { 0x41, 0x00, 0x00, 0x00, 0x00,
|
};
|
||||||
0x01, 0x00, 0x33, 0x00, 0x00 };
|
const uint8_t write_same_cdb_2[VIRTIO_SCSI_CDB_SIZE] = {
|
||||||
|
0x41, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
qvirtio_scsi_start("-drive file=blkdebug::null-co://,if=none,id=dr1"
|
qvirtio_scsi_start("-drive file=blkdebug::null-co://,if=none,id=dr1"
|
||||||
",format=raw,file.align=4k "
|
",format=raw,file.align=4k "
|
||||||
|
Loading…
Reference in New Issue
Block a user