1
2
3
4
5
6
7
8
9
10
11
12
13#include "block/block_int.h"
14
15#define NULL_OPT_LATENCY "latency-ns"
16
17typedef struct {
18 int64_t length;
19 int64_t latency_ns;
20} BDRVNullState;
21
22static QemuOptsList runtime_opts = {
23 .name = "null",
24 .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
25 .desc = {
26 {
27 .name = "filename",
28 .type = QEMU_OPT_STRING,
29 .help = "",
30 },
31 {
32 .name = BLOCK_OPT_SIZE,
33 .type = QEMU_OPT_SIZE,
34 .help = "size of the null block",
35 },
36 {
37 .name = NULL_OPT_LATENCY,
38 .type = QEMU_OPT_NUMBER,
39 .help = "nanoseconds (approximated) to wait "
40 "before completing request",
41 },
42 { }
43 },
44};
45
46static int null_file_open(BlockDriverState *bs, QDict *options, int flags,
47 Error **errp)
48{
49 QemuOpts *opts;
50 BDRVNullState *s = bs->opaque;
51 int ret = 0;
52
53 opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
54 qemu_opts_absorb_qdict(opts, options, &error_abort);
55 s->length =
56 qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 1 << 30);
57 s->latency_ns =
58 qemu_opt_get_number(opts, NULL_OPT_LATENCY, 0);
59 if (s->latency_ns < 0) {
60 error_setg(errp, "latency-ns is invalid");
61 ret = -EINVAL;
62 }
63 qemu_opts_del(opts);
64 return ret;
65}
66
67static void null_close(BlockDriverState *bs)
68{
69}
70
71static int64_t null_getlength(BlockDriverState *bs)
72{
73 BDRVNullState *s = bs->opaque;
74 return s->length;
75}
76
77static coroutine_fn int null_co_common(BlockDriverState *bs)
78{
79 BDRVNullState *s = bs->opaque;
80
81 if (s->latency_ns) {
82 co_aio_sleep_ns(bdrv_get_aio_context(bs), QEMU_CLOCK_REALTIME,
83 s->latency_ns);
84 }
85 return 0;
86}
87
88static coroutine_fn int null_co_readv(BlockDriverState *bs,
89 int64_t sector_num, int nb_sectors,
90 QEMUIOVector *qiov)
91{
92 return null_co_common(bs);
93}
94
95static coroutine_fn int null_co_writev(BlockDriverState *bs,
96 int64_t sector_num, int nb_sectors,
97 QEMUIOVector *qiov)
98{
99 return null_co_common(bs);
100}
101
102static coroutine_fn int null_co_flush(BlockDriverState *bs)
103{
104 return null_co_common(bs);
105}
106
107typedef struct {
108 BlockAIOCB common;
109 QEMUBH *bh;
110 QEMUTimer timer;
111} NullAIOCB;
112
113static const AIOCBInfo null_aiocb_info = {
114 .aiocb_size = sizeof(NullAIOCB),
115};
116
117static void null_bh_cb(void *opaque)
118{
119 NullAIOCB *acb = opaque;
120 acb->common.cb(acb->common.opaque, 0);
121 qemu_bh_delete(acb->bh);
122 qemu_aio_unref(acb);
123}
124
125static void null_timer_cb(void *opaque)
126{
127 NullAIOCB *acb = opaque;
128 acb->common.cb(acb->common.opaque, 0);
129 timer_deinit(&acb->timer);
130 qemu_aio_unref(acb);
131}
132
133static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
134 BlockCompletionFunc *cb,
135 void *opaque)
136{
137 NullAIOCB *acb;
138 BDRVNullState *s = bs->opaque;
139
140 acb = qemu_aio_get(&null_aiocb_info, bs, cb, opaque);
141
142 if (s->latency_ns) {
143 aio_timer_init(bdrv_get_aio_context(bs), &acb->timer,
144 QEMU_CLOCK_REALTIME, SCALE_NS,
145 null_timer_cb, acb);
146 timer_mod_ns(&acb->timer,
147 qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + s->latency_ns);
148 } else {
149 acb->bh = aio_bh_new(bdrv_get_aio_context(bs), null_bh_cb, acb);
150 qemu_bh_schedule(acb->bh);
151 }
152 return &acb->common;
153}
154
155static BlockAIOCB *null_aio_readv(BlockDriverState *bs,
156 int64_t sector_num, QEMUIOVector *qiov,
157 int nb_sectors,
158 BlockCompletionFunc *cb,
159 void *opaque)
160{
161 return null_aio_common(bs, cb, opaque);
162}
163
164static BlockAIOCB *null_aio_writev(BlockDriverState *bs,
165 int64_t sector_num, QEMUIOVector *qiov,
166 int nb_sectors,
167 BlockCompletionFunc *cb,
168 void *opaque)
169{
170 return null_aio_common(bs, cb, opaque);
171}
172
173static BlockAIOCB *null_aio_flush(BlockDriverState *bs,
174 BlockCompletionFunc *cb,
175 void *opaque)
176{
177 return null_aio_common(bs, cb, opaque);
178}
179
180static int null_reopen_prepare(BDRVReopenState *reopen_state,
181 BlockReopenQueue *queue, Error **errp)
182{
183 return 0;
184}
185
186static BlockDriver bdrv_null_co = {
187 .format_name = "null-co",
188 .protocol_name = "null-co",
189 .instance_size = sizeof(BDRVNullState),
190
191 .bdrv_file_open = null_file_open,
192 .bdrv_close = null_close,
193 .bdrv_getlength = null_getlength,
194
195 .bdrv_co_readv = null_co_readv,
196 .bdrv_co_writev = null_co_writev,
197 .bdrv_co_flush_to_disk = null_co_flush,
198 .bdrv_reopen_prepare = null_reopen_prepare,
199};
200
201static BlockDriver bdrv_null_aio = {
202 .format_name = "null-aio",
203 .protocol_name = "null-aio",
204 .instance_size = sizeof(BDRVNullState),
205
206 .bdrv_file_open = null_file_open,
207 .bdrv_close = null_close,
208 .bdrv_getlength = null_getlength,
209
210 .bdrv_aio_readv = null_aio_readv,
211 .bdrv_aio_writev = null_aio_writev,
212 .bdrv_aio_flush = null_aio_flush,
213 .bdrv_reopen_prepare = null_reopen_prepare,
214};
215
216static void bdrv_null_init(void)
217{
218 bdrv_register(&bdrv_null_co);
219 bdrv_register(&bdrv_null_aio);
220}
221
222block_init(bdrv_null_init);
223