xref: /openbmc/qemu/block/null.c (revision 8e6fe6b8)
1 /*
2  * Null block driver
3  *
4  * Authors:
5  *  Fam Zheng <famz@redhat.com>
6  *
7  * Copyright (C) 2014 Red Hat, Inc.
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  */
12 
13 #include "qemu/osdep.h"
14 #include "qapi/error.h"
15 #include "qapi/qmp/qdict.h"
16 #include "qapi/qmp/qstring.h"
17 #include "qemu/module.h"
18 #include "qemu/option.h"
19 #include "block/block_int.h"
20 
21 #define NULL_OPT_LATENCY "latency-ns"
22 #define NULL_OPT_ZEROES  "read-zeroes"
23 
24 typedef struct {
25     int64_t length;
26     int64_t latency_ns;
27     bool read_zeroes;
28 } BDRVNullState;
29 
30 static QemuOptsList runtime_opts = {
31     .name = "null",
32     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
33     .desc = {
34         {
35             .name = BLOCK_OPT_SIZE,
36             .type = QEMU_OPT_SIZE,
37             .help = "size of the null block",
38         },
39         {
40             .name = NULL_OPT_LATENCY,
41             .type = QEMU_OPT_NUMBER,
42             .help = "nanoseconds (approximated) to wait "
43                     "before completing request",
44         },
45         {
46             .name = NULL_OPT_ZEROES,
47             .type = QEMU_OPT_BOOL,
48             .help = "return zeroes when read",
49         },
50         { /* end of list */ }
51     },
52 };
53 
54 static void null_co_parse_filename(const char *filename, QDict *options,
55                                    Error **errp)
56 {
57     /* This functions only exists so that a null-co:// filename is accepted
58      * with the null-co driver. */
59     if (strcmp(filename, "null-co://")) {
60         error_setg(errp, "The only allowed filename for this driver is "
61                          "'null-co://'");
62         return;
63     }
64 }
65 
66 static void null_aio_parse_filename(const char *filename, QDict *options,
67                                     Error **errp)
68 {
69     /* This functions only exists so that a null-aio:// filename is accepted
70      * with the null-aio driver. */
71     if (strcmp(filename, "null-aio://")) {
72         error_setg(errp, "The only allowed filename for this driver is "
73                          "'null-aio://'");
74         return;
75     }
76 }
77 
78 static int null_file_open(BlockDriverState *bs, QDict *options, int flags,
79                           Error **errp)
80 {
81     QemuOpts *opts;
82     BDRVNullState *s = bs->opaque;
83     int ret = 0;
84 
85     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
86     qemu_opts_absorb_qdict(opts, options, &error_abort);
87     s->length =
88         qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 1 << 30);
89     s->latency_ns =
90         qemu_opt_get_number(opts, NULL_OPT_LATENCY, 0);
91     if (s->latency_ns < 0) {
92         error_setg(errp, "latency-ns is invalid");
93         ret = -EINVAL;
94     }
95     s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false);
96     qemu_opts_del(opts);
97     bs->supported_write_flags = BDRV_REQ_FUA;
98     return ret;
99 }
100 
101 static int64_t null_getlength(BlockDriverState *bs)
102 {
103     BDRVNullState *s = bs->opaque;
104     return s->length;
105 }
106 
107 static coroutine_fn int null_co_common(BlockDriverState *bs)
108 {
109     BDRVNullState *s = bs->opaque;
110 
111     if (s->latency_ns) {
112         qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, s->latency_ns);
113     }
114     return 0;
115 }
116 
117 static coroutine_fn int null_co_preadv(BlockDriverState *bs,
118                                        uint64_t offset, uint64_t bytes,
119                                        QEMUIOVector *qiov, int flags)
120 {
121     BDRVNullState *s = bs->opaque;
122 
123     if (s->read_zeroes) {
124         qemu_iovec_memset(qiov, 0, 0, bytes);
125     }
126 
127     return null_co_common(bs);
128 }
129 
130 static coroutine_fn int null_co_pwritev(BlockDriverState *bs,
131                                         uint64_t offset, uint64_t bytes,
132                                         QEMUIOVector *qiov, int flags)
133 {
134     return null_co_common(bs);
135 }
136 
137 static coroutine_fn int null_co_flush(BlockDriverState *bs)
138 {
139     return null_co_common(bs);
140 }
141 
142 typedef struct {
143     BlockAIOCB common;
144     QEMUTimer timer;
145 } NullAIOCB;
146 
147 static const AIOCBInfo null_aiocb_info = {
148     .aiocb_size = sizeof(NullAIOCB),
149 };
150 
151 static void null_bh_cb(void *opaque)
152 {
153     NullAIOCB *acb = opaque;
154     acb->common.cb(acb->common.opaque, 0);
155     qemu_aio_unref(acb);
156 }
157 
158 static void null_timer_cb(void *opaque)
159 {
160     NullAIOCB *acb = opaque;
161     acb->common.cb(acb->common.opaque, 0);
162     timer_deinit(&acb->timer);
163     qemu_aio_unref(acb);
164 }
165 
166 static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
167                                           BlockCompletionFunc *cb,
168                                           void *opaque)
169 {
170     NullAIOCB *acb;
171     BDRVNullState *s = bs->opaque;
172 
173     acb = qemu_aio_get(&null_aiocb_info, bs, cb, opaque);
174     /* Only emulate latency after vcpu is running. */
175     if (s->latency_ns) {
176         aio_timer_init(bdrv_get_aio_context(bs), &acb->timer,
177                        QEMU_CLOCK_REALTIME, SCALE_NS,
178                        null_timer_cb, acb);
179         timer_mod_ns(&acb->timer,
180                      qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + s->latency_ns);
181     } else {
182         aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), null_bh_cb, acb);
183     }
184     return &acb->common;
185 }
186 
187 static BlockAIOCB *null_aio_preadv(BlockDriverState *bs,
188                                    uint64_t offset, uint64_t bytes,
189                                    QEMUIOVector *qiov, int flags,
190                                    BlockCompletionFunc *cb,
191                                    void *opaque)
192 {
193     BDRVNullState *s = bs->opaque;
194 
195     if (s->read_zeroes) {
196         qemu_iovec_memset(qiov, 0, 0, bytes);
197     }
198 
199     return null_aio_common(bs, cb, opaque);
200 }
201 
202 static BlockAIOCB *null_aio_pwritev(BlockDriverState *bs,
203                                     uint64_t offset, uint64_t bytes,
204                                     QEMUIOVector *qiov, int flags,
205                                     BlockCompletionFunc *cb,
206                                     void *opaque)
207 {
208     return null_aio_common(bs, cb, opaque);
209 }
210 
211 static BlockAIOCB *null_aio_flush(BlockDriverState *bs,
212                                   BlockCompletionFunc *cb,
213                                   void *opaque)
214 {
215     return null_aio_common(bs, cb, opaque);
216 }
217 
218 static int null_reopen_prepare(BDRVReopenState *reopen_state,
219                                BlockReopenQueue *queue, Error **errp)
220 {
221     return 0;
222 }
223 
224 static int coroutine_fn null_co_block_status(BlockDriverState *bs,
225                                              bool want_zero, int64_t offset,
226                                              int64_t bytes, int64_t *pnum,
227                                              int64_t *map,
228                                              BlockDriverState **file)
229 {
230     BDRVNullState *s = bs->opaque;
231     int ret = BDRV_BLOCK_OFFSET_VALID;
232 
233     *pnum = bytes;
234     *map = offset;
235     *file = bs;
236 
237     if (s->read_zeroes) {
238         ret |= BDRV_BLOCK_ZERO;
239     }
240     return ret;
241 }
242 
243 static void null_refresh_filename(BlockDriverState *bs)
244 {
245     const QDictEntry *e;
246 
247     for (e = qdict_first(bs->full_open_options); e;
248          e = qdict_next(bs->full_open_options, e))
249     {
250         /* These options can be ignored */
251         if (strcmp(qdict_entry_key(e), "filename") &&
252             strcmp(qdict_entry_key(e), "driver") &&
253             strcmp(qdict_entry_key(e), NULL_OPT_LATENCY))
254         {
255             return;
256         }
257     }
258 
259     snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://",
260              bs->drv->format_name);
261 }
262 
263 static const char *const null_strong_runtime_opts[] = {
264     BLOCK_OPT_SIZE,
265     NULL_OPT_ZEROES,
266 
267     NULL
268 };
269 
270 static BlockDriver bdrv_null_co = {
271     .format_name            = "null-co",
272     .protocol_name          = "null-co",
273     .instance_size          = sizeof(BDRVNullState),
274 
275     .bdrv_file_open         = null_file_open,
276     .bdrv_parse_filename    = null_co_parse_filename,
277     .bdrv_getlength         = null_getlength,
278 
279     .bdrv_co_preadv         = null_co_preadv,
280     .bdrv_co_pwritev        = null_co_pwritev,
281     .bdrv_co_flush_to_disk  = null_co_flush,
282     .bdrv_reopen_prepare    = null_reopen_prepare,
283 
284     .bdrv_co_block_status   = null_co_block_status,
285 
286     .bdrv_refresh_filename  = null_refresh_filename,
287     .strong_runtime_opts    = null_strong_runtime_opts,
288 };
289 
290 static BlockDriver bdrv_null_aio = {
291     .format_name            = "null-aio",
292     .protocol_name          = "null-aio",
293     .instance_size          = sizeof(BDRVNullState),
294 
295     .bdrv_file_open         = null_file_open,
296     .bdrv_parse_filename    = null_aio_parse_filename,
297     .bdrv_getlength         = null_getlength,
298 
299     .bdrv_aio_preadv        = null_aio_preadv,
300     .bdrv_aio_pwritev       = null_aio_pwritev,
301     .bdrv_aio_flush         = null_aio_flush,
302     .bdrv_reopen_prepare    = null_reopen_prepare,
303 
304     .bdrv_co_block_status   = null_co_block_status,
305 
306     .bdrv_refresh_filename  = null_refresh_filename,
307     .strong_runtime_opts    = null_strong_runtime_opts,
308 };
309 
310 static void bdrv_null_init(void)
311 {
312     bdrv_register(&bdrv_null_co);
313     bdrv_register(&bdrv_null_aio);
314 }
315 
316 block_init(bdrv_null_init);
317