qemu/block/null.c
<<
>>
Prefs
   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
  23typedef struct {
  24    int64_t length;
  25    int64_t latency_ns;
  26    bool read_zeroes;
  27} BDRVNullState;
  28
  29static 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
  53static 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
  65static 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
  77static 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
 100static int64_t null_getlength(BlockDriverState *bs)
 101{
 102    BDRVNullState *s = bs->opaque;
 103    return s->length;
 104}
 105
 106static 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
 116static 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
 129static 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
 136static coroutine_fn int null_co_flush(BlockDriverState *bs)
 137{
 138    return null_co_common(bs);
 139}
 140
 141typedef struct {
 142    BlockAIOCB common;
 143    QEMUTimer timer;
 144} NullAIOCB;
 145
 146static const AIOCBInfo null_aiocb_info = {
 147    .aiocb_size = sizeof(NullAIOCB),
 148};
 149
 150static 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
 157static 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
 165static 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
 186static 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
 201static 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
 210static BlockAIOCB *null_aio_flush(BlockDriverState *bs,
 211                                  BlockCompletionFunc *cb,
 212                                  void *opaque)
 213{
 214    return null_aio_common(bs, cb, opaque);
 215}
 216
 217static int null_reopen_prepare(BDRVReopenState *reopen_state,
 218                               BlockReopenQueue *queue, Error **errp)
 219{
 220    return 0;
 221}
 222
 223static 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
 242static 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
 255static 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
 274static 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
 293static void bdrv_null_init(void)
 294{
 295    bdrv_register(&bdrv_null_co);
 296    bdrv_register(&bdrv_null_aio);
 297}
 298
 299block_init(bdrv_null_init);
 300