1 | /* |
2 | * Block protocol for block driver correctness testing |
3 | * |
4 | * Copyright (C) 2010 IBM, Corp. |
5 | * |
6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
7 | * See the COPYING file in the top-level directory. |
8 | */ |
9 | |
10 | #include "qemu/osdep.h" |
11 | #include "qapi/error.h" |
12 | #include "qemu/sockets.h" /* for EINPROGRESS on Windows */ |
13 | #include "block/block_int.h" |
14 | #include "qapi/qmp/qdict.h" |
15 | #include "qapi/qmp/qstring.h" |
16 | #include "qemu/cutils.h" |
17 | #include "qemu/module.h" |
18 | #include "qemu/option.h" |
19 | |
20 | typedef struct { |
21 | BdrvChild *test_file; |
22 | } BDRVBlkverifyState; |
23 | |
24 | typedef struct BlkverifyRequest { |
25 | Coroutine *co; |
26 | BlockDriverState *bs; |
27 | |
28 | /* Request metadata */ |
29 | bool is_write; |
30 | uint64_t offset; |
31 | uint64_t bytes; |
32 | int flags; |
33 | |
34 | int (*request_fn)(BdrvChild *, int64_t, unsigned int, QEMUIOVector *, |
35 | BdrvRequestFlags); |
36 | |
37 | int ret; /* test image result */ |
38 | int raw_ret; /* raw image result */ |
39 | |
40 | unsigned int done; /* completion counter */ |
41 | |
42 | QEMUIOVector *qiov; /* user I/O vector */ |
43 | QEMUIOVector *raw_qiov; /* cloned I/O vector for raw file */ |
44 | } BlkverifyRequest; |
45 | |
46 | static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyRequest *r, |
47 | const char *fmt, ...) |
48 | { |
49 | va_list ap; |
50 | |
51 | va_start(ap, fmt); |
52 | fprintf(stderr, "blkverify: %s offset=%" PRId64 " bytes=%" PRId64 " " , |
53 | r->is_write ? "write" : "read" , r->offset, r->bytes); |
54 | vfprintf(stderr, fmt, ap); |
55 | fprintf(stderr, "\n" ); |
56 | va_end(ap); |
57 | exit(1); |
58 | } |
59 | |
60 | /* Valid blkverify filenames look like blkverify:path/to/raw_image:path/to/image */ |
61 | static void blkverify_parse_filename(const char *filename, QDict *options, |
62 | Error **errp) |
63 | { |
64 | const char *c; |
65 | QString *raw_path; |
66 | |
67 | |
68 | /* Parse the blkverify: prefix */ |
69 | if (!strstart(filename, "blkverify:" , &filename)) { |
70 | /* There was no prefix; therefore, all options have to be already |
71 | present in the QDict (except for the filename) */ |
72 | qdict_put_str(options, "x-image" , filename); |
73 | return; |
74 | } |
75 | |
76 | /* Parse the raw image filename */ |
77 | c = strchr(filename, ':'); |
78 | if (c == NULL) { |
79 | error_setg(errp, "blkverify requires raw copy and original image path" ); |
80 | return; |
81 | } |
82 | |
83 | /* TODO Implement option pass-through and set raw.filename here */ |
84 | raw_path = qstring_from_substr(filename, 0, c - filename); |
85 | qdict_put(options, "x-raw" , raw_path); |
86 | |
87 | /* TODO Allow multi-level nesting and set file.filename here */ |
88 | filename = c + 1; |
89 | qdict_put_str(options, "x-image" , filename); |
90 | } |
91 | |
92 | static QemuOptsList runtime_opts = { |
93 | .name = "blkverify" , |
94 | .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), |
95 | .desc = { |
96 | { |
97 | .name = "x-raw" , |
98 | .type = QEMU_OPT_STRING, |
99 | .help = "[internal use only, will be removed]" , |
100 | }, |
101 | { |
102 | .name = "x-image" , |
103 | .type = QEMU_OPT_STRING, |
104 | .help = "[internal use only, will be removed]" , |
105 | }, |
106 | { /* end of list */ } |
107 | }, |
108 | }; |
109 | |
110 | static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, |
111 | Error **errp) |
112 | { |
113 | BDRVBlkverifyState *s = bs->opaque; |
114 | QemuOpts *opts; |
115 | Error *local_err = NULL; |
116 | int ret; |
117 | |
118 | opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); |
119 | qemu_opts_absorb_qdict(opts, options, &local_err); |
120 | if (local_err) { |
121 | error_propagate(errp, local_err); |
122 | ret = -EINVAL; |
123 | goto fail; |
124 | } |
125 | |
126 | /* Open the raw file */ |
127 | bs->file = bdrv_open_child(qemu_opt_get(opts, "x-raw" ), options, "raw" , |
128 | bs, &child_file, false, &local_err); |
129 | if (local_err) { |
130 | ret = -EINVAL; |
131 | error_propagate(errp, local_err); |
132 | goto fail; |
133 | } |
134 | |
135 | /* Open the test file */ |
136 | s->test_file = bdrv_open_child(qemu_opt_get(opts, "x-image" ), options, |
137 | "test" , bs, &child_format, false, |
138 | &local_err); |
139 | if (local_err) { |
140 | ret = -EINVAL; |
141 | error_propagate(errp, local_err); |
142 | goto fail; |
143 | } |
144 | |
145 | bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; |
146 | bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; |
147 | |
148 | ret = 0; |
149 | fail: |
150 | qemu_opts_del(opts); |
151 | return ret; |
152 | } |
153 | |
154 | static void blkverify_close(BlockDriverState *bs) |
155 | { |
156 | BDRVBlkverifyState *s = bs->opaque; |
157 | |
158 | bdrv_unref_child(bs, s->test_file); |
159 | s->test_file = NULL; |
160 | } |
161 | |
162 | static int64_t blkverify_getlength(BlockDriverState *bs) |
163 | { |
164 | BDRVBlkverifyState *s = bs->opaque; |
165 | |
166 | return bdrv_getlength(s->test_file->bs); |
167 | } |
168 | |
169 | static void coroutine_fn blkverify_do_test_req(void *opaque) |
170 | { |
171 | BlkverifyRequest *r = opaque; |
172 | BDRVBlkverifyState *s = r->bs->opaque; |
173 | |
174 | r->ret = r->request_fn(s->test_file, r->offset, r->bytes, r->qiov, |
175 | r->flags); |
176 | r->done++; |
177 | qemu_coroutine_enter_if_inactive(r->co); |
178 | } |
179 | |
180 | static void coroutine_fn blkverify_do_raw_req(void *opaque) |
181 | { |
182 | BlkverifyRequest *r = opaque; |
183 | |
184 | r->raw_ret = r->request_fn(r->bs->file, r->offset, r->bytes, r->raw_qiov, |
185 | r->flags); |
186 | r->done++; |
187 | qemu_coroutine_enter_if_inactive(r->co); |
188 | } |
189 | |
190 | static int coroutine_fn |
191 | blkverify_co_prwv(BlockDriverState *bs, BlkverifyRequest *r, uint64_t offset, |
192 | uint64_t bytes, QEMUIOVector *qiov, QEMUIOVector *raw_qiov, |
193 | int flags, bool is_write) |
194 | { |
195 | Coroutine *co_a, *co_b; |
196 | |
197 | *r = (BlkverifyRequest) { |
198 | .co = qemu_coroutine_self(), |
199 | .bs = bs, |
200 | .offset = offset, |
201 | .bytes = bytes, |
202 | .qiov = qiov, |
203 | .raw_qiov = raw_qiov, |
204 | .flags = flags, |
205 | .is_write = is_write, |
206 | .request_fn = is_write ? bdrv_co_pwritev : bdrv_co_preadv, |
207 | }; |
208 | |
209 | co_a = qemu_coroutine_create(blkverify_do_test_req, r); |
210 | co_b = qemu_coroutine_create(blkverify_do_raw_req, r); |
211 | |
212 | qemu_coroutine_enter(co_a); |
213 | qemu_coroutine_enter(co_b); |
214 | |
215 | while (r->done < 2) { |
216 | qemu_coroutine_yield(); |
217 | } |
218 | |
219 | if (r->ret != r->raw_ret) { |
220 | blkverify_err(r, "return value mismatch %d != %d" , r->ret, r->raw_ret); |
221 | } |
222 | |
223 | return r->ret; |
224 | } |
225 | |
226 | static int coroutine_fn |
227 | blkverify_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, |
228 | QEMUIOVector *qiov, int flags) |
229 | { |
230 | BlkverifyRequest r; |
231 | QEMUIOVector raw_qiov; |
232 | void *buf; |
233 | ssize_t cmp_offset; |
234 | int ret; |
235 | |
236 | buf = qemu_blockalign(bs->file->bs, qiov->size); |
237 | qemu_iovec_init(&raw_qiov, qiov->niov); |
238 | qemu_iovec_clone(&raw_qiov, qiov, buf); |
239 | |
240 | ret = blkverify_co_prwv(bs, &r, offset, bytes, qiov, &raw_qiov, flags, |
241 | false); |
242 | |
243 | cmp_offset = qemu_iovec_compare(qiov, &raw_qiov); |
244 | if (cmp_offset != -1) { |
245 | blkverify_err(&r, "contents mismatch at offset %" PRId64, |
246 | offset + cmp_offset); |
247 | } |
248 | |
249 | qemu_iovec_destroy(&raw_qiov); |
250 | qemu_vfree(buf); |
251 | |
252 | return ret; |
253 | } |
254 | |
255 | static int coroutine_fn |
256 | blkverify_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, |
257 | QEMUIOVector *qiov, int flags) |
258 | { |
259 | BlkverifyRequest r; |
260 | return blkverify_co_prwv(bs, &r, offset, bytes, qiov, qiov, flags, true); |
261 | } |
262 | |
263 | static int blkverify_co_flush(BlockDriverState *bs) |
264 | { |
265 | BDRVBlkverifyState *s = bs->opaque; |
266 | |
267 | /* Only flush test file, the raw file is not important */ |
268 | return bdrv_co_flush(s->test_file->bs); |
269 | } |
270 | |
271 | static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs, |
272 | BlockDriverState *candidate) |
273 | { |
274 | BDRVBlkverifyState *s = bs->opaque; |
275 | |
276 | bool perm = bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); |
277 | |
278 | if (perm) { |
279 | return true; |
280 | } |
281 | |
282 | return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate); |
283 | } |
284 | |
285 | static void blkverify_refresh_filename(BlockDriverState *bs) |
286 | { |
287 | BDRVBlkverifyState *s = bs->opaque; |
288 | |
289 | if (bs->file->bs->exact_filename[0] |
290 | && s->test_file->bs->exact_filename[0]) |
291 | { |
292 | int ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename), |
293 | "blkverify:%s:%s" , |
294 | bs->file->bs->exact_filename, |
295 | s->test_file->bs->exact_filename); |
296 | if (ret >= sizeof(bs->exact_filename)) { |
297 | /* An overflow makes the filename unusable, so do not report any */ |
298 | bs->exact_filename[0] = 0; |
299 | } |
300 | } |
301 | } |
302 | |
303 | static char *blkverify_dirname(BlockDriverState *bs, Error **errp) |
304 | { |
305 | /* In general, there are two BDSs with different dirnames below this one; |
306 | * so there is no unique dirname we could return (unless both are equal by |
307 | * chance). Therefore, to be consistent, just always return NULL. */ |
308 | error_setg(errp, "Cannot generate a base directory for blkverify nodes" ); |
309 | return NULL; |
310 | } |
311 | |
312 | static BlockDriver bdrv_blkverify = { |
313 | .format_name = "blkverify" , |
314 | .protocol_name = "blkverify" , |
315 | .instance_size = sizeof(BDRVBlkverifyState), |
316 | |
317 | .bdrv_parse_filename = blkverify_parse_filename, |
318 | .bdrv_file_open = blkverify_open, |
319 | .bdrv_close = blkverify_close, |
320 | .bdrv_child_perm = bdrv_filter_default_perms, |
321 | .bdrv_getlength = blkverify_getlength, |
322 | .bdrv_refresh_filename = blkverify_refresh_filename, |
323 | .bdrv_dirname = blkverify_dirname, |
324 | |
325 | .bdrv_co_preadv = blkverify_co_preadv, |
326 | .bdrv_co_pwritev = blkverify_co_pwritev, |
327 | .bdrv_co_flush = blkverify_co_flush, |
328 | |
329 | .is_filter = true, |
330 | .bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter, |
331 | }; |
332 | |
333 | static void bdrv_blkverify_init(void) |
334 | { |
335 | bdrv_register(&bdrv_blkverify); |
336 | } |
337 | |
338 | block_init(bdrv_blkverify_init); |
339 | |