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    return ret;
  97}
  98
  99static void null_close(BlockDriverState *bs)
 100{
 101}
 102
 103static int64_t null_getlength(BlockDriverState *bs)
 104{
 105    BDRVNullState *s = bs->opaque;
 106    return s->length;
 107}
 108
 109static coroutine_fn int null_co_common(BlockDriverState *bs)
 110{
 111    BDRVNullState *s = bs->opaque;
 112
 113    if (s->latency_ns) {
 114        qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, s->latency_ns);
 115    }
 116    return 0;
 117}
 118
 119static coroutine_fn int null_co_readv(BlockDriverState *bs,
 120                                      int64_t sector_num, int nb_sectors,
 121                                      QEMUIOVector *qiov)
 122{
 123    BDRVNullState *s = bs->opaque;
 124
 125    if (s->read_zeroes) {
 126        qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE);
 127    }
 128
 129    return null_co_common(bs);
 130}
 131
 132static coroutine_fn int null_co_writev(BlockDriverState *bs,
 133                                       int64_t sector_num, int nb_sectors,
 134                                       QEMUIOVector *qiov)
 135{
 136    return null_co_common(bs);
 137}
 138
 139static coroutine_fn int null_co_flush(BlockDriverState *bs)
 140{
 141    return null_co_common(bs);
 142}
 143
 144typedef struct {
 145    BlockAIOCB common;
 146    QEMUTimer timer;
 147} NullAIOCB;
 148
 149static const AIOCBInfo null_aiocb_info = {
 150    .aiocb_size = sizeof(NullAIOCB),
 151};
 152
 153static void null_bh_cb(void *opaque)
 154{
 155    NullAIOCB *acb = opaque;
 156    acb->common.cb(acb->common.opaque, 0);
 157    qemu_aio_unref(acb);
 158}
 159
 160static void null_timer_cb(void *opaque)
 161{
 162    NullAIOCB *acb = opaque;
 163    acb->common.cb(acb->common.opaque, 0);
 164    timer_deinit(&acb->timer);
 165    qemu_aio_unref(acb);
 166}
 167
 168static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
 169                                          BlockCompletionFunc *cb,
 170                                          void *opaque)
 171{
 172    NullAIOCB *acb;
 173    BDRVNullState *s = bs->opaque;
 174
 175    acb = qemu_aio_get(&null_aiocb_info, bs, cb, opaque);
 176    /* Only emulate latency after vcpu is running. */
 177    if (s->latency_ns) {
 178        aio_timer_init(bdrv_get_aio_context(bs), &acb->timer,
 179                       QEMU_CLOCK_REALTIME, SCALE_NS,
 180                       null_timer_cb, acb);
 181        timer_mod_ns(&acb->timer,
 182                     qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + s->latency_ns);
 183    } else {
 184        aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), null_bh_cb, acb);
 185    }
 186    return &acb->common;
 187}
 188
 189static BlockAIOCB *null_aio_readv(BlockDriverState *bs,
 190                                  int64_t sector_num, QEMUIOVector *qiov,
 191                                  int nb_sectors,
 192                                  BlockCompletionFunc *cb,
 193                                  void *opaque)
 194{
 195    BDRVNullState *s = bs->opaque;
 196
 197    if (s->read_zeroes) {
 198        qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE);
 199    }
 200
 201    return null_aio_common(bs, cb, opaque);
 202}
 203
 204static BlockAIOCB *null_aio_writev(BlockDriverState *bs,
 205                                   int64_t sector_num, QEMUIOVector *qiov,
 206                                   int nb_sectors,
 207                                   BlockCompletionFunc *cb,
 208                                   void *opaque)
 209{
 210    return null_aio_common(bs, cb, opaque);
 211}
 212
 213static BlockAIOCB *null_aio_flush(BlockDriverState *bs,
 214                                  BlockCompletionFunc *cb,
 215                                  void *opaque)
 216{
 217    return null_aio_common(bs, cb, opaque);
 218}
 219
 220static int null_reopen_prepare(BDRVReopenState *reopen_state,
 221                               BlockReopenQueue *queue, Error **errp)
 222{
 223    return 0;
 224}
 225
 226static int coroutine_fn null_co_block_status(BlockDriverState *bs,
 227                                             bool want_zero, int64_t offset,
 228                                             int64_t bytes, int64_t *pnum,
 229                                             int64_t *map,
 230                                             BlockDriverState **file)
 231{
 232    BDRVNullState *s = bs->opaque;
 233    int ret = BDRV_BLOCK_OFFSET_VALID;
 234
 235    *pnum = bytes;
 236    *map = offset;
 237    *file = bs;
 238
 239    if (s->read_zeroes) {
 240        ret |= BDRV_BLOCK_ZERO;
 241    }
 242    return ret;
 243}
 244
 245static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
 246{
 247    QINCREF(opts);
 248    qdict_del(opts, "filename");
 249
 250    if (!qdict_size(opts)) {
 251        snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://",
 252                 bs->drv->format_name);
 253    }
 254
 255    qdict_put_str(opts, "driver", bs->drv->format_name);
 256    bs->full_open_options = opts;
 257}
 258
 259static BlockDriver bdrv_null_co = {
 260    .format_name            = "null-co",
 261    .protocol_name          = "null-co",
 262    .instance_size          = sizeof(BDRVNullState),
 263
 264    .bdrv_file_open         = null_file_open,
 265    .bdrv_parse_filename    = null_co_parse_filename,
 266    .bdrv_close             = null_close,
 267    .bdrv_getlength         = null_getlength,
 268
 269    .bdrv_co_readv          = null_co_readv,
 270    .bdrv_co_writev         = null_co_writev,
 271    .bdrv_co_flush_to_disk  = null_co_flush,
 272    .bdrv_reopen_prepare    = null_reopen_prepare,
 273
 274    .bdrv_co_block_status   = null_co_block_status,
 275
 276    .bdrv_refresh_filename  = null_refresh_filename,
 277};
 278
 279static BlockDriver bdrv_null_aio = {
 280    .format_name            = "null-aio",
 281    .protocol_name          = "null-aio",
 282    .instance_size          = sizeof(BDRVNullState),
 283
 284    .bdrv_file_open         = null_file_open,
 285    .bdrv_parse_filename    = null_aio_parse_filename,
 286    .bdrv_close             = null_close,
 287    .bdrv_getlength         = null_getlength,
 288
 289    .bdrv_aio_readv         = null_aio_readv,
 290    .bdrv_aio_writev        = null_aio_writev,
 291    .bdrv_aio_flush         = null_aio_flush,
 292    .bdrv_reopen_prepare    = null_reopen_prepare,
 293
 294    .bdrv_co_block_status   = null_co_block_status,
 295
 296    .bdrv_refresh_filename  = null_refresh_filename,
 297};
 298
 299static void bdrv_null_init(void)
 300{
 301    bdrv_register(&bdrv_null_co);
 302    bdrv_register(&bdrv_null_aio);
 303}
 304
 305block_init(bdrv_null_init);
 306