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