]>
Commit | Line | Data |
---|---|---|
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 | ||
18 | typedef struct { | |
19 | BdrvChild *test_file; | |
20 | } BDRVBlkverifyState; | |
21 | ||
22 | typedef struct BlkverifyAIOCB BlkverifyAIOCB; | |
23 | struct BlkverifyAIOCB { | |
24 | BlockAIOCB common; | |
25 | ||
26 | /* Request metadata */ | |
27 | bool is_write; | |
28 | int64_t sector_num; | |
29 | int nb_sectors; | |
30 | ||
31 | int ret; /* first completed request's result */ | |
32 | unsigned int done; /* completion counter */ | |
33 | ||
34 | QEMUIOVector *qiov; /* user I/O vector */ | |
35 | QEMUIOVector raw_qiov; /* cloned I/O vector for raw file */ | |
36 | void *buf; /* buffer for raw file I/O */ | |
37 | ||
38 | void (*verify)(BlkverifyAIOCB *acb); | |
39 | }; | |
40 | ||
41 | static const AIOCBInfo blkverify_aiocb_info = { | |
42 | .aiocb_size = sizeof(BlkverifyAIOCB), | |
43 | }; | |
44 | ||
45 | static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb, | |
46 | const char *fmt, ...) | |
47 | { | |
48 | va_list ap; | |
49 | ||
50 | va_start(ap, fmt); | |
51 | fprintf(stderr, "blkverify: %s sector_num=%" PRId64 " nb_sectors=%d ", | |
52 | acb->is_write ? "write" : "read", acb->sector_num, | |
53 | acb->nb_sectors); | |
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(options, "x-image", qstring_from_str(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 - 1); | |
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(options, "x-image", qstring_from_str(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 | ret = 0; | |
146 | fail: | |
147 | if (ret < 0) { | |
148 | bdrv_unref_child(bs, bs->file); | |
149 | } | |
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 BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write, | |
170 | int64_t sector_num, QEMUIOVector *qiov, | |
171 | int nb_sectors, | |
172 | BlockCompletionFunc *cb, | |
173 | void *opaque) | |
174 | { | |
175 | BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aiocb_info, bs, cb, opaque); | |
176 | ||
177 | acb->is_write = is_write; | |
178 | acb->sector_num = sector_num; | |
179 | acb->nb_sectors = nb_sectors; | |
180 | acb->ret = -EINPROGRESS; | |
181 | acb->done = 0; | |
182 | acb->qiov = qiov; | |
183 | acb->buf = NULL; | |
184 | acb->verify = NULL; | |
185 | return acb; | |
186 | } | |
187 | ||
188 | static void blkverify_aio_bh(void *opaque) | |
189 | { | |
190 | BlkverifyAIOCB *acb = opaque; | |
191 | ||
192 | if (acb->buf) { | |
193 | qemu_iovec_destroy(&acb->raw_qiov); | |
194 | qemu_vfree(acb->buf); | |
195 | } | |
196 | acb->common.cb(acb->common.opaque, acb->ret); | |
197 | qemu_aio_unref(acb); | |
198 | } | |
199 | ||
200 | static void blkverify_aio_cb(void *opaque, int ret) | |
201 | { | |
202 | BlkverifyAIOCB *acb = opaque; | |
203 | ||
204 | switch (++acb->done) { | |
205 | case 1: | |
206 | acb->ret = ret; | |
207 | break; | |
208 | ||
209 | case 2: | |
210 | if (acb->ret != ret) { | |
211 | blkverify_err(acb, "return value mismatch %d != %d", acb->ret, ret); | |
212 | } | |
213 | ||
214 | if (acb->verify) { | |
215 | acb->verify(acb); | |
216 | } | |
217 | ||
218 | aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs), | |
219 | blkverify_aio_bh, acb); | |
220 | break; | |
221 | } | |
222 | } | |
223 | ||
224 | static void blkverify_verify_readv(BlkverifyAIOCB *acb) | |
225 | { | |
226 | ssize_t offset = qemu_iovec_compare(acb->qiov, &acb->raw_qiov); | |
227 | if (offset != -1) { | |
228 | blkverify_err(acb, "contents mismatch in sector %" PRId64, | |
229 | acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE)); | |
230 | } | |
231 | } | |
232 | ||
233 | static BlockAIOCB *blkverify_aio_readv(BlockDriverState *bs, | |
234 | int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | |
235 | BlockCompletionFunc *cb, void *opaque) | |
236 | { | |
237 | BDRVBlkverifyState *s = bs->opaque; | |
238 | BlkverifyAIOCB *acb = blkverify_aio_get(bs, false, sector_num, qiov, | |
239 | nb_sectors, cb, opaque); | |
240 | ||
241 | acb->verify = blkverify_verify_readv; | |
242 | acb->buf = qemu_blockalign(bs->file->bs, qiov->size); | |
243 | qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov); | |
244 | qemu_iovec_clone(&acb->raw_qiov, qiov, acb->buf); | |
245 | ||
246 | bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors, | |
247 | blkverify_aio_cb, acb); | |
248 | bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors, | |
249 | blkverify_aio_cb, acb); | |
250 | return &acb->common; | |
251 | } | |
252 | ||
253 | static BlockAIOCB *blkverify_aio_writev(BlockDriverState *bs, | |
254 | int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | |
255 | BlockCompletionFunc *cb, void *opaque) | |
256 | { | |
257 | BDRVBlkverifyState *s = bs->opaque; | |
258 | BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov, | |
259 | nb_sectors, cb, opaque); | |
260 | ||
261 | bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors, | |
262 | blkverify_aio_cb, acb); | |
263 | bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, | |
264 | blkverify_aio_cb, acb); | |
265 | return &acb->common; | |
266 | } | |
267 | ||
268 | static BlockAIOCB *blkverify_aio_flush(BlockDriverState *bs, | |
269 | BlockCompletionFunc *cb, | |
270 | void *opaque) | |
271 | { | |
272 | BDRVBlkverifyState *s = bs->opaque; | |
273 | ||
274 | /* Only flush test file, the raw file is not important */ | |
275 | return bdrv_aio_flush(s->test_file->bs, cb, opaque); | |
276 | } | |
277 | ||
278 | static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs, | |
279 | BlockDriverState *candidate) | |
280 | { | |
281 | BDRVBlkverifyState *s = bs->opaque; | |
282 | ||
283 | bool perm = bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); | |
284 | ||
285 | if (perm) { | |
286 | return true; | |
287 | } | |
288 | ||
289 | return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate); | |
290 | } | |
291 | ||
292 | static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options) | |
293 | { | |
294 | BDRVBlkverifyState *s = bs->opaque; | |
295 | ||
296 | /* bs->file->bs has already been refreshed */ | |
297 | bdrv_refresh_filename(s->test_file->bs); | |
298 | ||
299 | if (bs->file->bs->full_open_options | |
300 | && s->test_file->bs->full_open_options) | |
301 | { | |
302 | QDict *opts = qdict_new(); | |
303 | qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkverify"))); | |
304 | ||
305 | QINCREF(bs->file->bs->full_open_options); | |
306 | qdict_put_obj(opts, "raw", QOBJECT(bs->file->bs->full_open_options)); | |
307 | QINCREF(s->test_file->bs->full_open_options); | |
308 | qdict_put_obj(opts, "test", | |
309 | QOBJECT(s->test_file->bs->full_open_options)); | |
310 | ||
311 | bs->full_open_options = opts; | |
312 | } | |
313 | ||
314 | if (bs->file->bs->exact_filename[0] | |
315 | && s->test_file->bs->exact_filename[0]) | |
316 | { | |
317 | snprintf(bs->exact_filename, sizeof(bs->exact_filename), | |
318 | "blkverify:%s:%s", | |
319 | bs->file->bs->exact_filename, | |
320 | s->test_file->bs->exact_filename); | |
321 | } | |
322 | } | |
323 | ||
324 | static BlockDriver bdrv_blkverify = { | |
325 | .format_name = "blkverify", | |
326 | .protocol_name = "blkverify", | |
327 | .instance_size = sizeof(BDRVBlkverifyState), | |
328 | ||
329 | .bdrv_parse_filename = blkverify_parse_filename, | |
330 | .bdrv_file_open = blkverify_open, | |
331 | .bdrv_close = blkverify_close, | |
332 | .bdrv_getlength = blkverify_getlength, | |
333 | .bdrv_refresh_filename = blkverify_refresh_filename, | |
334 | ||
335 | .bdrv_aio_readv = blkverify_aio_readv, | |
336 | .bdrv_aio_writev = blkverify_aio_writev, | |
337 | .bdrv_aio_flush = blkverify_aio_flush, | |
338 | ||
339 | .is_filter = true, | |
340 | .bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter, | |
341 | }; | |
342 | ||
343 | static void bdrv_blkverify_init(void) | |
344 | { | |
345 | bdrv_register(&bdrv_blkverify); | |
346 | } | |
347 | ||
348 | block_init(bdrv_blkverify_init); |