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/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
  24typedef struct {
  25    int64_t length;
  26    int64_t latency_ns;
  27    bool read_zeroes;
  28} BDRVNullState;
  29
  30static 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
  54static 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
  66static 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
  78static 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
 101static int64_t null_getlength(BlockDriverState *bs)
 102{
 103    BDRVNullState *s = bs->opaque;
 104    return s->length;
 105}
 106
 107static 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
 117static 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
 130static 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
 137static coroutine_fn int null_co_flush(BlockDriverState *bs)
 138{
 139    return null_co_common(bs);
 140}
 141
 142typedef struct {
 143    BlockAIOCB common;
 144    QEMUTimer timer;
 145} NullAIOCB;
 146
 147static const AIOCBInfo null_aiocb_info = {
 148    .aiocb_size = sizeof(NullAIOCB),
 149};
 150
 151static 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
 158static 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
 166static 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
 187static 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
 202static 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
 211static BlockAIOCB *null_aio_flush(BlockDriverState *bs,
 212                                  BlockCompletionFunc *cb,
 213                                  void *opaque)
 214{
 215    return null_aio_common(bs, cb, opaque);
 216}
 217
 218static int null_reopen_prepare(BDRVReopenState *reopen_state,
 219                               BlockReopenQueue *queue, Error **errp)
 220{
 221    return 0;
 222}
 223
 224static 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
 243static 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
 263static const char *const null_strong_runtime_opts[] = {
 264    BLOCK_OPT_SIZE,
 265    NULL_OPT_ZEROES,
 266
 267    NULL
 268};
 269
 270static 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
 290static 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
 310static void bdrv_null_init(void)
 311{
 312    bdrv_register(&bdrv_null_co);
 313    bdrv_register(&bdrv_null_aio);
 314}
 315
 316block_init(bdrv_null_init);
 317