xref: /openbmc/qemu/block/blkverify.c (revision 121d0712)
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     QEMUBH *bh;
26 
27     /* Request metadata */
28     bool is_write;
29     int64_t sector_num;
30     int nb_sectors;
31 
32     int ret;                    /* first completed request's result */
33     unsigned int done;          /* completion counter */
34 
35     QEMUIOVector *qiov;         /* user I/O vector */
36     QEMUIOVector raw_qiov;      /* cloned I/O vector for raw file */
37     void *buf;                  /* buffer for raw file I/O */
38 
39     void (*verify)(BlkverifyAIOCB *acb);
40 };
41 
42 static const AIOCBInfo blkverify_aiocb_info = {
43     .aiocb_size         = sizeof(BlkverifyAIOCB),
44 };
45 
46 static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb,
47                                              const char *fmt, ...)
48 {
49     va_list ap;
50 
51     va_start(ap, fmt);
52     fprintf(stderr, "blkverify: %s sector_num=%" PRId64 " nb_sectors=%d ",
53             acb->is_write ? "write" : "read", acb->sector_num,
54             acb->nb_sectors);
55     vfprintf(stderr, fmt, ap);
56     fprintf(stderr, "\n");
57     va_end(ap);
58     exit(1);
59 }
60 
61 /* Valid blkverify filenames look like blkverify:path/to/raw_image:path/to/image */
62 static void blkverify_parse_filename(const char *filename, QDict *options,
63                                      Error **errp)
64 {
65     const char *c;
66     QString *raw_path;
67 
68 
69     /* Parse the blkverify: prefix */
70     if (!strstart(filename, "blkverify:", &filename)) {
71         /* There was no prefix; therefore, all options have to be already
72            present in the QDict (except for the filename) */
73         qdict_put(options, "x-image", qstring_from_str(filename));
74         return;
75     }
76 
77     /* Parse the raw image filename */
78     c = strchr(filename, ':');
79     if (c == NULL) {
80         error_setg(errp, "blkverify requires raw copy and original image path");
81         return;
82     }
83 
84     /* TODO Implement option pass-through and set raw.filename here */
85     raw_path = qstring_from_substr(filename, 0, c - filename - 1);
86     qdict_put(options, "x-raw", raw_path);
87 
88     /* TODO Allow multi-level nesting and set file.filename here */
89     filename = c + 1;
90     qdict_put(options, "x-image", qstring_from_str(filename));
91 }
92 
93 static QemuOptsList runtime_opts = {
94     .name = "blkverify",
95     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
96     .desc = {
97         {
98             .name = "x-raw",
99             .type = QEMU_OPT_STRING,
100             .help = "[internal use only, will be removed]",
101         },
102         {
103             .name = "x-image",
104             .type = QEMU_OPT_STRING,
105             .help = "[internal use only, will be removed]",
106         },
107         { /* end of list */ }
108     },
109 };
110 
111 static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
112                           Error **errp)
113 {
114     BDRVBlkverifyState *s = bs->opaque;
115     QemuOpts *opts;
116     Error *local_err = NULL;
117     int ret;
118 
119     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
120     qemu_opts_absorb_qdict(opts, options, &local_err);
121     if (local_err) {
122         error_propagate(errp, local_err);
123         ret = -EINVAL;
124         goto fail;
125     }
126 
127     /* Open the raw file */
128     bs->file = bdrv_open_child(qemu_opt_get(opts, "x-raw"), options, "raw",
129                                bs, &child_file, false, &local_err);
130     if (local_err) {
131         ret = -EINVAL;
132         error_propagate(errp, local_err);
133         goto fail;
134     }
135 
136     /* Open the test file */
137     s->test_file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options,
138                                    "test", bs, &child_format, false,
139                                    &local_err);
140     if (local_err) {
141         ret = -EINVAL;
142         error_propagate(errp, local_err);
143         goto fail;
144     }
145 
146     ret = 0;
147 fail:
148     if (ret < 0) {
149         bdrv_unref_child(bs, bs->file);
150     }
151     qemu_opts_del(opts);
152     return ret;
153 }
154 
155 static void blkverify_close(BlockDriverState *bs)
156 {
157     BDRVBlkverifyState *s = bs->opaque;
158 
159     bdrv_unref_child(bs, s->test_file);
160     s->test_file = NULL;
161 }
162 
163 static int64_t blkverify_getlength(BlockDriverState *bs)
164 {
165     BDRVBlkverifyState *s = bs->opaque;
166 
167     return bdrv_getlength(s->test_file->bs);
168 }
169 
170 static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
171                                          int64_t sector_num, QEMUIOVector *qiov,
172                                          int nb_sectors,
173                                          BlockCompletionFunc *cb,
174                                          void *opaque)
175 {
176     BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aiocb_info, bs, cb, opaque);
177 
178     acb->bh = NULL;
179     acb->is_write = is_write;
180     acb->sector_num = sector_num;
181     acb->nb_sectors = nb_sectors;
182     acb->ret = -EINPROGRESS;
183     acb->done = 0;
184     acb->qiov = qiov;
185     acb->buf = NULL;
186     acb->verify = NULL;
187     return acb;
188 }
189 
190 static void blkverify_aio_bh(void *opaque)
191 {
192     BlkverifyAIOCB *acb = opaque;
193 
194     qemu_bh_delete(acb->bh);
195     if (acb->buf) {
196         qemu_iovec_destroy(&acb->raw_qiov);
197         qemu_vfree(acb->buf);
198     }
199     acb->common.cb(acb->common.opaque, acb->ret);
200     qemu_aio_unref(acb);
201 }
202 
203 static void blkverify_aio_cb(void *opaque, int ret)
204 {
205     BlkverifyAIOCB *acb = opaque;
206 
207     switch (++acb->done) {
208     case 1:
209         acb->ret = ret;
210         break;
211 
212     case 2:
213         if (acb->ret != ret) {
214             blkverify_err(acb, "return value mismatch %d != %d", acb->ret, ret);
215         }
216 
217         if (acb->verify) {
218             acb->verify(acb);
219         }
220 
221         acb->bh = aio_bh_new(bdrv_get_aio_context(acb->common.bs),
222                              blkverify_aio_bh, acb);
223         qemu_bh_schedule(acb->bh);
224         break;
225     }
226 }
227 
228 static void blkverify_verify_readv(BlkverifyAIOCB *acb)
229 {
230     ssize_t offset = qemu_iovec_compare(acb->qiov, &acb->raw_qiov);
231     if (offset != -1) {
232         blkverify_err(acb, "contents mismatch in sector %" PRId64,
233                       acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE));
234     }
235 }
236 
237 static BlockAIOCB *blkverify_aio_readv(BlockDriverState *bs,
238         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
239         BlockCompletionFunc *cb, void *opaque)
240 {
241     BDRVBlkverifyState *s = bs->opaque;
242     BlkverifyAIOCB *acb = blkverify_aio_get(bs, false, sector_num, qiov,
243                                             nb_sectors, cb, opaque);
244 
245     acb->verify = blkverify_verify_readv;
246     acb->buf = qemu_blockalign(bs->file->bs, qiov->size);
247     qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov);
248     qemu_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
249 
250     bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
251                    blkverify_aio_cb, acb);
252     bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors,
253                    blkverify_aio_cb, acb);
254     return &acb->common;
255 }
256 
257 static BlockAIOCB *blkverify_aio_writev(BlockDriverState *bs,
258         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
259         BlockCompletionFunc *cb, void *opaque)
260 {
261     BDRVBlkverifyState *s = bs->opaque;
262     BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov,
263                                             nb_sectors, cb, opaque);
264 
265     bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors,
266                     blkverify_aio_cb, acb);
267     bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
268                     blkverify_aio_cb, acb);
269     return &acb->common;
270 }
271 
272 static BlockAIOCB *blkverify_aio_flush(BlockDriverState *bs,
273                                        BlockCompletionFunc *cb,
274                                        void *opaque)
275 {
276     BDRVBlkverifyState *s = bs->opaque;
277 
278     /* Only flush test file, the raw file is not important */
279     return bdrv_aio_flush(s->test_file->bs, cb, opaque);
280 }
281 
282 static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
283                                                   BlockDriverState *candidate)
284 {
285     BDRVBlkverifyState *s = bs->opaque;
286 
287     bool perm = bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
288 
289     if (perm) {
290         return true;
291     }
292 
293     return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
294 }
295 
296 static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
297 {
298     BDRVBlkverifyState *s = bs->opaque;
299 
300     /* bs->file->bs has already been refreshed */
301     bdrv_refresh_filename(s->test_file->bs);
302 
303     if (bs->file->bs->full_open_options
304         && s->test_file->bs->full_open_options)
305     {
306         QDict *opts = qdict_new();
307         qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkverify")));
308 
309         QINCREF(bs->file->bs->full_open_options);
310         qdict_put_obj(opts, "raw", QOBJECT(bs->file->bs->full_open_options));
311         QINCREF(s->test_file->bs->full_open_options);
312         qdict_put_obj(opts, "test",
313                       QOBJECT(s->test_file->bs->full_open_options));
314 
315         bs->full_open_options = opts;
316     }
317 
318     if (bs->file->bs->exact_filename[0]
319         && s->test_file->bs->exact_filename[0])
320     {
321         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
322                  "blkverify:%s:%s",
323                  bs->file->bs->exact_filename,
324                  s->test_file->bs->exact_filename);
325     }
326 }
327 
328 static BlockDriver bdrv_blkverify = {
329     .format_name                      = "blkverify",
330     .protocol_name                    = "blkverify",
331     .instance_size                    = sizeof(BDRVBlkverifyState),
332 
333     .bdrv_parse_filename              = blkverify_parse_filename,
334     .bdrv_file_open                   = blkverify_open,
335     .bdrv_close                       = blkverify_close,
336     .bdrv_getlength                   = blkverify_getlength,
337     .bdrv_refresh_filename            = blkverify_refresh_filename,
338 
339     .bdrv_aio_readv                   = blkverify_aio_readv,
340     .bdrv_aio_writev                  = blkverify_aio_writev,
341     .bdrv_aio_flush                   = blkverify_aio_flush,
342 
343     .is_filter                        = true,
344     .bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter,
345 };
346 
347 static void bdrv_blkverify_init(void)
348 {
349     bdrv_register(&bdrv_blkverify);
350 }
351 
352 block_init(bdrv_blkverify_init);
353