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 "block/block_int.h"
  18
  19#define NULL_OPT_LATENCY "latency-ns"
  20#define NULL_OPT_ZEROES  "read-zeroes"
  21
  22typedef struct {
  23    int64_t length;
  24    int64_t latency_ns;
  25    bool read_zeroes;
  26} BDRVNullState;
  27
  28static QemuOptsList runtime_opts = {
  29    .name = "null",
  30    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
  31    .desc = {
  32        {
  33            .name = BLOCK_OPT_SIZE,
  34            .type = QEMU_OPT_SIZE,
  35            .help = "size of the null block",
  36        },
  37        {
  38            .name = NULL_OPT_LATENCY,
  39            .type = QEMU_OPT_NUMBER,
  40            .help = "nanoseconds (approximated) to wait "
  41                    "before completing request",
  42        },
  43        {
  44            .name = NULL_OPT_ZEROES,
  45            .type = QEMU_OPT_BOOL,
  46            .help = "return zeroes when read",
  47        },
  48        { /* end of list */ }
  49    },
  50};
  51
  52static void null_co_parse_filename(const char *filename, QDict *options,
  53                                   Error **errp)
  54{
  55    /* This functions only exists so that a null-co:// filename is accepted
  56     * with the null-co driver. */
  57    if (strcmp(filename, "null-co://")) {
  58        error_setg(errp, "The only allowed filename for this driver is "
  59                         "'null-co://'");
  60        return;
  61    }
  62}
  63
  64static void null_aio_parse_filename(const char *filename, QDict *options,
  65                                    Error **errp)
  66{
  67    /* This functions only exists so that a null-aio:// filename is accepted
  68     * with the null-aio driver. */
  69    if (strcmp(filename, "null-aio://")) {
  70        error_setg(errp, "The only allowed filename for this driver is "
  71                         "'null-aio://'");
  72        return;
  73    }
  74}
  75
  76static int null_file_open(BlockDriverState *bs, QDict *options, int flags,
  77                          Error **errp)
  78{
  79    QemuOpts *opts;
  80    BDRVNullState *s = bs->opaque;
  81    int ret = 0;
  82
  83    opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
  84    qemu_opts_absorb_qdict(opts, options, &error_abort);
  85    s->length =
  86        qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 1 << 30);
  87    s->latency_ns =
  88        qemu_opt_get_number(opts, NULL_OPT_LATENCY, 0);
  89    if (s->latency_ns < 0) {
  90        error_setg(errp, "latency-ns is invalid");
  91        ret = -EINVAL;
  92    }
  93    s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false);
  94    qemu_opts_del(opts);
  95    return ret;
  96}
  97
  98static void null_close(BlockDriverState *bs)
  99{
 100}
 101
 102static int64_t null_getlength(BlockDriverState *bs)
 103{
 104    BDRVNullState *s = bs->opaque;
 105    return s->length;
 106}
 107
 108static coroutine_fn int null_co_common(BlockDriverState *bs)
 109{
 110    BDRVNullState *s = bs->opaque;
 111
 112    if (s->latency_ns) {
 113        co_aio_sleep_ns(bdrv_get_aio_context(bs), QEMU_CLOCK_REALTIME,
 114                        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 int64_t coroutine_fn null_co_get_block_status(BlockDriverState *bs,
 227                                                     int64_t sector_num,
 228                                                     int nb_sectors, int *pnum,
 229                                                     BlockDriverState **file)
 230{
 231    BDRVNullState *s = bs->opaque;
 232    off_t start = sector_num * BDRV_SECTOR_SIZE;
 233
 234    *pnum = nb_sectors;
 235    *file = bs;
 236
 237    if (s->read_zeroes) {
 238        return BDRV_BLOCK_OFFSET_VALID | start | BDRV_BLOCK_ZERO;
 239    } else {
 240        return BDRV_BLOCK_OFFSET_VALID | start;
 241    }
 242}
 243
 244static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
 245{
 246    QINCREF(opts);
 247    qdict_del(opts, "filename");
 248
 249    if (!qdict_size(opts)) {
 250        snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://",
 251                 bs->drv->format_name);
 252    }
 253
 254    qdict_put_str(opts, "driver", bs->drv->format_name);
 255    bs->full_open_options = opts;
 256}
 257
 258static BlockDriver bdrv_null_co = {
 259    .format_name            = "null-co",
 260    .protocol_name          = "null-co",
 261    .instance_size          = sizeof(BDRVNullState),
 262
 263    .bdrv_file_open         = null_file_open,
 264    .bdrv_parse_filename    = null_co_parse_filename,
 265    .bdrv_close             = null_close,
 266    .bdrv_getlength         = null_getlength,
 267
 268    .bdrv_co_readv          = null_co_readv,
 269    .bdrv_co_writev         = null_co_writev,
 270    .bdrv_co_flush_to_disk  = null_co_flush,
 271    .bdrv_reopen_prepare    = null_reopen_prepare,
 272
 273    .bdrv_co_get_block_status   = null_co_get_block_status,
 274
 275    .bdrv_refresh_filename  = null_refresh_filename,
 276};
 277
 278static BlockDriver bdrv_null_aio = {
 279    .format_name            = "null-aio",
 280    .protocol_name          = "null-aio",
 281    .instance_size          = sizeof(BDRVNullState),
 282
 283    .bdrv_file_open         = null_file_open,
 284    .bdrv_parse_filename    = null_aio_parse_filename,
 285    .bdrv_close             = null_close,
 286    .bdrv_getlength         = null_getlength,
 287
 288    .bdrv_aio_readv         = null_aio_readv,
 289    .bdrv_aio_writev        = null_aio_writev,
 290    .bdrv_aio_flush         = null_aio_flush,
 291    .bdrv_reopen_prepare    = null_reopen_prepare,
 292
 293    .bdrv_co_get_block_status   = null_co_get_block_status,
 294
 295    .bdrv_refresh_filename  = null_refresh_filename,
 296};
 297
 298static void bdrv_null_init(void)
 299{
 300    bdrv_register(&bdrv_null_co);
 301    bdrv_register(&bdrv_null_aio);
 302}
 303
 304block_init(bdrv_null_init);
 305