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/module.h" |
14 | #include "block/block_int.h" |
15 | #include "sysemu/replay.h" |
16 | #include "qapi/error.h" |
17 | |
18 | typedef struct Request { |
19 | Coroutine *co; |
20 | QEMUBH *bh; |
21 | } Request; |
22 | |
23 | static 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 | bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; |
39 | bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; |
40 | |
41 | ret = 0; |
42 | fail: |
43 | return ret; |
44 | } |
45 | |
46 | static int64_t blkreplay_getlength(BlockDriverState *bs) |
47 | { |
48 | return bdrv_getlength(bs->file->bs); |
49 | } |
50 | |
51 | /* This bh is used for synchronization of return from coroutines. |
52 | It continues yielded coroutine which then finishes its execution. |
53 | BH is called adjusted to some replay checkpoint, therefore |
54 | record and replay will always finish coroutines deterministically. |
55 | */ |
56 | static void blkreplay_bh_cb(void *opaque) |
57 | { |
58 | Request *req = opaque; |
59 | aio_co_wake(req->co); |
60 | qemu_bh_delete(req->bh); |
61 | g_free(req); |
62 | } |
63 | |
64 | static void block_request_create(uint64_t reqid, BlockDriverState *bs, |
65 | Coroutine *co) |
66 | { |
67 | Request *req = g_new(Request, 1); |
68 | *req = (Request) { |
69 | .co = co, |
70 | .bh = aio_bh_new(bdrv_get_aio_context(bs), blkreplay_bh_cb, req), |
71 | }; |
72 | replay_block_event(req->bh, reqid); |
73 | } |
74 | |
75 | static int coroutine_fn blkreplay_co_preadv(BlockDriverState *bs, |
76 | uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) |
77 | { |
78 | uint64_t reqid = blkreplay_next_id(); |
79 | int ret = bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); |
80 | block_request_create(reqid, bs, qemu_coroutine_self()); |
81 | qemu_coroutine_yield(); |
82 | |
83 | return ret; |
84 | } |
85 | |
86 | static int coroutine_fn blkreplay_co_pwritev(BlockDriverState *bs, |
87 | uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) |
88 | { |
89 | uint64_t reqid = blkreplay_next_id(); |
90 | int ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); |
91 | block_request_create(reqid, bs, qemu_coroutine_self()); |
92 | qemu_coroutine_yield(); |
93 | |
94 | return ret; |
95 | } |
96 | |
97 | static int coroutine_fn blkreplay_co_pwrite_zeroes(BlockDriverState *bs, |
98 | int64_t offset, int bytes, BdrvRequestFlags flags) |
99 | { |
100 | uint64_t reqid = blkreplay_next_id(); |
101 | int ret = bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); |
102 | block_request_create(reqid, bs, qemu_coroutine_self()); |
103 | qemu_coroutine_yield(); |
104 | |
105 | return ret; |
106 | } |
107 | |
108 | static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs, |
109 | int64_t offset, int bytes) |
110 | { |
111 | uint64_t reqid = blkreplay_next_id(); |
112 | int ret = bdrv_co_pdiscard(bs->file, offset, bytes); |
113 | block_request_create(reqid, bs, qemu_coroutine_self()); |
114 | qemu_coroutine_yield(); |
115 | |
116 | return ret; |
117 | } |
118 | |
119 | static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs) |
120 | { |
121 | uint64_t reqid = blkreplay_next_id(); |
122 | int ret = bdrv_co_flush(bs->file->bs); |
123 | block_request_create(reqid, bs, qemu_coroutine_self()); |
124 | qemu_coroutine_yield(); |
125 | |
126 | return ret; |
127 | } |
128 | |
129 | static BlockDriver bdrv_blkreplay = { |
130 | .format_name = "blkreplay" , |
131 | .instance_size = 0, |
132 | |
133 | .bdrv_open = blkreplay_open, |
134 | .bdrv_child_perm = bdrv_filter_default_perms, |
135 | .bdrv_getlength = blkreplay_getlength, |
136 | |
137 | .bdrv_co_preadv = blkreplay_co_preadv, |
138 | .bdrv_co_pwritev = blkreplay_co_pwritev, |
139 | |
140 | .bdrv_co_pwrite_zeroes = blkreplay_co_pwrite_zeroes, |
141 | .bdrv_co_pdiscard = blkreplay_co_pdiscard, |
142 | .bdrv_co_flush = blkreplay_co_flush, |
143 | }; |
144 | |
145 | static void bdrv_blkreplay_init(void) |
146 | { |
147 | bdrv_register(&bdrv_blkreplay); |
148 | } |
149 | |
150 | block_init(bdrv_blkreplay_init); |
151 | |