qemu/block/blkreplay.c
<<
>>
Prefs
   1/*
   2 * Block protocol for record/replay
   3 *
   4 * Copyright (c) 2010-2016 Institute for System Programming
   5 *                         of the Russian Academy of Sciences.
   6 *
   7 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   8 * See the COPYING file in the top-level directory.
   9 *
  10 */
  11
  12#include "qemu/osdep.h"
  13#include "qemu-common.h"
  14#include "block/block_int.h"
  15#include "sysemu/replay.h"
  16#include "qapi/error.h"
  17
  18typedef struct Request {
  19    Coroutine *co;
  20    QEMUBH *bh;
  21} Request;
  22
  23static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags,
  24                          Error **errp)
  25{
  26    Error *local_err = NULL;
  27    int ret;
  28
  29    /* Open the image file */
  30    bs->file = bdrv_open_child(NULL, options, "image",
  31                               bs, &child_file, false, &local_err);
  32    if (local_err) {
  33        ret = -EINVAL;
  34        error_propagate(errp, local_err);
  35        goto fail;
  36    }
  37
  38    ret = 0;
  39fail:
  40    return ret;
  41}
  42
  43static void blkreplay_close(BlockDriverState *bs)
  44{
  45}
  46
  47static int64_t blkreplay_getlength(BlockDriverState *bs)
  48{
  49    return bdrv_getlength(bs->file->bs);
  50}
  51
  52/* This bh is used for synchronization of return from coroutines.
  53   It continues yielded coroutine which then finishes its execution.
  54   BH is called adjusted to some replay checkpoint, therefore
  55   record and replay will always finish coroutines deterministically.
  56*/
  57static void blkreplay_bh_cb(void *opaque)
  58{
  59    Request *req = opaque;
  60    aio_co_wake(req->co);
  61    qemu_bh_delete(req->bh);
  62    g_free(req);
  63}
  64
  65static void block_request_create(uint64_t reqid, BlockDriverState *bs,
  66                                 Coroutine *co)
  67{
  68    Request *req = g_new(Request, 1);
  69    *req = (Request) {
  70        .co = co,
  71        .bh = aio_bh_new(bdrv_get_aio_context(bs), blkreplay_bh_cb, req),
  72    };
  73    replay_block_event(req->bh, reqid);
  74}
  75
  76static int coroutine_fn blkreplay_co_preadv(BlockDriverState *bs,
  77    uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)
  78{
  79    uint64_t reqid = blkreplay_next_id();
  80    int ret = bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
  81    block_request_create(reqid, bs, qemu_coroutine_self());
  82    qemu_coroutine_yield();
  83
  84    return ret;
  85}
  86
  87static int coroutine_fn blkreplay_co_pwritev(BlockDriverState *bs,
  88    uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)
  89{
  90    uint64_t reqid = blkreplay_next_id();
  91    int ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
  92    block_request_create(reqid, bs, qemu_coroutine_self());
  93    qemu_coroutine_yield();
  94
  95    return ret;
  96}
  97
  98static int coroutine_fn blkreplay_co_pwrite_zeroes(BlockDriverState *bs,
  99    int64_t offset, int bytes, BdrvRequestFlags flags)
 100{
 101    uint64_t reqid = blkreplay_next_id();
 102    int ret = bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
 103    block_request_create(reqid, bs, qemu_coroutine_self());
 104    qemu_coroutine_yield();
 105
 106    return ret;
 107}
 108
 109static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs,
 110                                              int64_t offset, int bytes)
 111{
 112    uint64_t reqid = blkreplay_next_id();
 113    int ret = bdrv_co_pdiscard(bs->file->bs, offset, bytes);
 114    block_request_create(reqid, bs, qemu_coroutine_self());
 115    qemu_coroutine_yield();
 116
 117    return ret;
 118}
 119
 120static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs)
 121{
 122    uint64_t reqid = blkreplay_next_id();
 123    int ret = bdrv_co_flush(bs->file->bs);
 124    block_request_create(reqid, bs, qemu_coroutine_self());
 125    qemu_coroutine_yield();
 126
 127    return ret;
 128}
 129
 130static BlockDriver bdrv_blkreplay = {
 131    .format_name            = "blkreplay",
 132    .instance_size          = 0,
 133
 134    .bdrv_open              = blkreplay_open,
 135    .bdrv_close             = blkreplay_close,
 136    .bdrv_child_perm        = bdrv_filter_default_perms,
 137    .bdrv_getlength         = blkreplay_getlength,
 138
 139    .bdrv_co_preadv         = blkreplay_co_preadv,
 140    .bdrv_co_pwritev        = blkreplay_co_pwritev,
 141
 142    .bdrv_co_pwrite_zeroes  = blkreplay_co_pwrite_zeroes,
 143    .bdrv_co_pdiscard       = blkreplay_co_pdiscard,
 144    .bdrv_co_flush          = blkreplay_co_flush,
 145};
 146
 147static void bdrv_blkreplay_init(void)
 148{
 149    bdrv_register(&bdrv_blkreplay);
 150}
 151
 152block_init(bdrv_blkreplay_init);
 153