block/null: Latency simulation by adding new option "latency-ns"
Aio context switch should just work because the requests will be drained, so the scheduled timer(s) on the old context will be freed. Signed-off-by: Fam Zheng <famz@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Message-id: 1427852740-24315-2-git-send-email-famz@redhat.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
9eddd6a4b3
commit
e5e51dd3af
54
block/null.c
54
block/null.c
@ -12,8 +12,11 @@
|
|||||||
|
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
|
||||||
|
#define NULL_OPT_LATENCY "latency-ns"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int64_t length;
|
int64_t length;
|
||||||
|
int64_t latency_ns;
|
||||||
} BDRVNullState;
|
} BDRVNullState;
|
||||||
|
|
||||||
static QemuOptsList runtime_opts = {
|
static QemuOptsList runtime_opts = {
|
||||||
@ -30,6 +33,12 @@ static QemuOptsList runtime_opts = {
|
|||||||
.type = QEMU_OPT_SIZE,
|
.type = QEMU_OPT_SIZE,
|
||||||
.help = "size of the null block",
|
.help = "size of the null block",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = NULL_OPT_LATENCY,
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "nanoseconds (approximated) to wait "
|
||||||
|
"before completing request",
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -39,13 +48,20 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
{
|
{
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
BDRVNullState *s = bs->opaque;
|
BDRVNullState *s = bs->opaque;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &error_abort);
|
qemu_opts_absorb_qdict(opts, options, &error_abort);
|
||||||
s->length =
|
s->length =
|
||||||
qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 1 << 30);
|
qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 1 << 30);
|
||||||
|
s->latency_ns =
|
||||||
|
qemu_opt_get_number(opts, NULL_OPT_LATENCY, 0);
|
||||||
|
if (s->latency_ns < 0) {
|
||||||
|
error_setg(errp, "latency-ns is invalid");
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void null_close(BlockDriverState *bs)
|
static void null_close(BlockDriverState *bs)
|
||||||
@ -58,28 +74,40 @@ static int64_t null_getlength(BlockDriverState *bs)
|
|||||||
return s->length;
|
return s->length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static coroutine_fn int null_co_common(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVNullState *s = bs->opaque;
|
||||||
|
|
||||||
|
if (s->latency_ns) {
|
||||||
|
co_aio_sleep_ns(bdrv_get_aio_context(bs), QEMU_CLOCK_REALTIME,
|
||||||
|
s->latency_ns);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static coroutine_fn int null_co_readv(BlockDriverState *bs,
|
static coroutine_fn int null_co_readv(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors,
|
int64_t sector_num, int nb_sectors,
|
||||||
QEMUIOVector *qiov)
|
QEMUIOVector *qiov)
|
||||||
{
|
{
|
||||||
return 0;
|
return null_co_common(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static coroutine_fn int null_co_writev(BlockDriverState *bs,
|
static coroutine_fn int null_co_writev(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors,
|
int64_t sector_num, int nb_sectors,
|
||||||
QEMUIOVector *qiov)
|
QEMUIOVector *qiov)
|
||||||
{
|
{
|
||||||
return 0;
|
return null_co_common(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static coroutine_fn int null_co_flush(BlockDriverState *bs)
|
static coroutine_fn int null_co_flush(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
return 0;
|
return null_co_common(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
BlockAIOCB common;
|
BlockAIOCB common;
|
||||||
QEMUBH *bh;
|
QEMUBH *bh;
|
||||||
|
QEMUTimer timer;
|
||||||
} NullAIOCB;
|
} NullAIOCB;
|
||||||
|
|
||||||
static const AIOCBInfo null_aiocb_info = {
|
static const AIOCBInfo null_aiocb_info = {
|
||||||
@ -94,15 +122,33 @@ static void null_bh_cb(void *opaque)
|
|||||||
qemu_aio_unref(acb);
|
qemu_aio_unref(acb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void null_timer_cb(void *opaque)
|
||||||
|
{
|
||||||
|
NullAIOCB *acb = opaque;
|
||||||
|
acb->common.cb(acb->common.opaque, 0);
|
||||||
|
timer_deinit(&acb->timer);
|
||||||
|
qemu_aio_unref(acb);
|
||||||
|
}
|
||||||
|
|
||||||
static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
|
static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
|
||||||
BlockCompletionFunc *cb,
|
BlockCompletionFunc *cb,
|
||||||
void *opaque)
|
void *opaque)
|
||||||
{
|
{
|
||||||
NullAIOCB *acb;
|
NullAIOCB *acb;
|
||||||
|
BDRVNullState *s = bs->opaque;
|
||||||
|
|
||||||
acb = qemu_aio_get(&null_aiocb_info, bs, cb, opaque);
|
acb = qemu_aio_get(&null_aiocb_info, bs, cb, opaque);
|
||||||
|
/* Only emulate latency after vcpu is running. */
|
||||||
|
if (s->latency_ns) {
|
||||||
|
aio_timer_init(bdrv_get_aio_context(bs), &acb->timer,
|
||||||
|
QEMU_CLOCK_REALTIME, SCALE_NS,
|
||||||
|
null_timer_cb, acb);
|
||||||
|
timer_mod_ns(&acb->timer,
|
||||||
|
qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + s->latency_ns);
|
||||||
|
} else {
|
||||||
acb->bh = aio_bh_new(bdrv_get_aio_context(bs), null_bh_cb, acb);
|
acb->bh = aio_bh_new(bdrv_get_aio_context(bs), null_bh_cb, acb);
|
||||||
qemu_bh_schedule(acb->bh);
|
qemu_bh_schedule(acb->bh);
|
||||||
|
}
|
||||||
return &acb->common;
|
return &acb->common;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1310,11 +1310,14 @@
|
|||||||
# Driver specific block device options for the null backend.
|
# Driver specific block device options for the null backend.
|
||||||
#
|
#
|
||||||
# @size: #optional size of the device in bytes.
|
# @size: #optional size of the device in bytes.
|
||||||
|
# @latency-ns: #optional emulated latency (in nanoseconds) in processing
|
||||||
|
# requests. Default to zero which completes requests immediately.
|
||||||
|
# (Since 2.4)
|
||||||
#
|
#
|
||||||
# Since: 2.2
|
# Since: 2.2
|
||||||
##
|
##
|
||||||
{ 'type': 'BlockdevOptionsNull',
|
{ 'type': 'BlockdevOptionsNull',
|
||||||
'data': { '*size': 'int' } }
|
'data': { '*size': 'int', '*latency-ns': 'uint64' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockdevOptionsVVFAT
|
# @BlockdevOptionsVVFAT
|
||||||
|
Loading…
Reference in New Issue
Block a user