debuggers.hg

view tools/blktap2/drivers/tapdisk-diff.c @ 22848:6341fe0f4e5a

Added tag 4.1.0-rc2 for changeset 9dca60d88c63
author Keir Fraser <keir@xen.org>
date Tue Jan 25 14:06:55 2011 +0000 (2011-01-25)
parents 9009f9f76441
children
line source
1 /*
2 * Copyright (c) 2008, XenSource Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of XenSource Inc. nor the names of its contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
20 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 #include <stdio.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <assert.h>
34 #include <libgen.h> /* for basename(3) */
35 #include <unistd.h>
37 #include "list.h"
38 #include "scheduler.h"
39 #include "tapdisk-vbd.h"
40 #include "tapdisk-server.h"
41 #include "tapdisk-disktype.h"
42 #include "libvhd.h"
44 #define POLL_READ 0
45 #define POLL_WRITE 1
47 #define SPB_SHIFT (VHD_BLOCK_SHIFT - SECTOR_SHIFT)
49 struct tapdisk_stream_poll {
50 int pipe[2];
51 int set;
52 };
54 struct tapdisk_stream_request {
55 uint64_t sec;
56 uint32_t secs;
57 uint64_t seqno;
58 blkif_request_t blkif_req;
59 struct list_head next;
60 };
62 struct tapdisk_stream {
63 td_vbd_t *vbd;
65 unsigned int id;
67 int err;
69 uint64_t cur;
70 uint64_t start;
71 uint64_t end;
73 uint64_t started;
74 uint64_t completed;
76 struct tapdisk_stream_poll poll;
77 event_id_t enqueue_event_id;
79 struct list_head free_list;
80 struct list_head pending_list;
81 struct list_head completed_list;
83 struct tapdisk_stream_request requests[MAX_REQUESTS];
84 };
86 static unsigned int tapdisk_stream_count;
88 static void tapdisk_stream_close_image(struct tapdisk_stream *);
90 static char *program;
91 static struct tapdisk_stream stream1, stream2;
92 static vhd_context_t vhd1;
94 static void
95 usage(FILE *stream)
96 {
97 printf("usage: %s <-n type:/path/to/image> <-m type:/path/to/image>\n",
98 program);
99 }
101 static int
102 open_vhd(const char *path, vhd_context_t *vhd)
103 {
104 int err;
106 err = vhd_open(vhd, path, VHD_OPEN_RDONLY);
107 if (err) {
108 printf("error opening %s: %d\n", path, err);
109 return err;
110 }
112 err = vhd_get_bat(vhd);
113 if (err)
114 {
115 printf("error reading BAT for %s: %d\n", path, err);
116 vhd_close(vhd);
117 return err;
118 }
120 return 0;
121 }
123 static inline void
124 tapdisk_stream_poll_initialize(struct tapdisk_stream_poll *p)
125 {
126 p->set = 0;
127 p->pipe[POLL_READ] = p->pipe[POLL_WRITE] = -1;
128 }
130 static int
131 tapdisk_stream_poll_open(struct tapdisk_stream_poll *p)
132 {
133 int err;
135 tapdisk_stream_poll_initialize(p);
137 err = pipe(p->pipe);
138 if (err)
139 return -errno;
141 err = fcntl(p->pipe[POLL_READ], F_SETFL, O_NONBLOCK);
142 if (err)
143 goto out;
145 err = fcntl(p->pipe[POLL_WRITE], F_SETFL, O_NONBLOCK);
146 if (err)
147 goto out;
149 return 0;
151 out:
152 close(p->pipe[POLL_READ]);
153 close(p->pipe[POLL_WRITE]);
154 tapdisk_stream_poll_initialize(p);
155 return -errno;
156 }
158 static void
159 tapdisk_stream_poll_close(struct tapdisk_stream_poll *p)
160 {
161 if (p->pipe[POLL_READ] != -1)
162 close(p->pipe[POLL_READ]);
163 if (p->pipe[POLL_WRITE] != -1)
164 close(p->pipe[POLL_WRITE]);
165 tapdisk_stream_poll_initialize(p);
166 }
168 static inline void
169 tapdisk_stream_poll_clear(struct tapdisk_stream_poll *p)
170 {
171 int dummy;
173 read(p->pipe[POLL_READ], &dummy, sizeof(dummy));
174 p->set = 0;
175 }
177 static inline void
178 tapdisk_stream_poll_set(struct tapdisk_stream_poll *p)
179 {
180 int dummy = 0;
182 if (!p->set) {
183 write(p->pipe[POLL_WRITE], &dummy, sizeof(dummy));
184 p->set = 1;
185 }
186 }
188 static inline int
189 tapdisk_stream_stop(struct tapdisk_stream *s)
190 {
191 return ((s->cur == s->end || s->err) &&
192 list_empty(&s->pending_list) &&
193 list_empty(&s->completed_list));
194 }
196 static inline void
197 tapdisk_stream_initialize_request(struct tapdisk_stream_request *req)
198 {
199 memset(req, 0, sizeof(*req));
200 INIT_LIST_HEAD(&req->next);
201 }
203 static inline int
204 tapdisk_stream_request_idx(struct tapdisk_stream *s,
205 struct tapdisk_stream_request *req)
206 {
207 return (req - s->requests);
208 }
210 static inline struct tapdisk_stream_request *
211 tapdisk_stream_get_request(struct tapdisk_stream *s)
212 {
213 struct tapdisk_stream_request *req;
215 if (list_empty(&s->free_list))
216 return NULL;
218 req = list_entry(s->free_list.next,
219 struct tapdisk_stream_request, next);
221 list_del_init(&req->next);
222 tapdisk_stream_initialize_request(req);
224 return req;
225 }
227 static inline void
228 tapdisk_stream_queue_completed(struct tapdisk_stream *s,
229 struct tapdisk_stream_request *sreq)
230 {
231 struct tapdisk_stream_request *itr;
233 list_for_each_entry(itr, &s->completed_list, next)
234 if (sreq->seqno < itr->seqno) {
235 list_add_tail(&sreq->next, &itr->next);
236 return;
237 }
239 list_add_tail(&sreq->next, &s->completed_list);
240 }
242 static int
243 tapdisk_result_compare(struct tapdisk_stream_request *sreq1,
244 struct tapdisk_stream_request *sreq2)
245 {
246 unsigned long idx1, idx2;
247 char *buf1, *buf2;
248 int result;
250 assert(sreq1->seqno == sreq2->seqno);
251 assert(sreq1->secs == sreq2->secs);
252 idx1 = (unsigned long)tapdisk_stream_request_idx(&stream1,
253 sreq1);
254 idx2 = (unsigned long)tapdisk_stream_request_idx(&stream2,
255 sreq2);
256 buf1 = (char *)MMAP_VADDR(stream1.vbd->ring.vstart, idx1, 0);
257 buf2 = (char *)MMAP_VADDR(stream2.vbd->ring.vstart, idx2, 0);
259 result = memcmp(buf1, buf2, sreq1->secs << SECTOR_SHIFT);
260 return result;
261 }
263 static int
264 tapdisk_stream_process_data(void)
265 {
266 struct tapdisk_stream_request *sreq1, *sreq2, *tmp1, *tmp2;
267 int advance_both;
268 int result = 0;
270 sreq1 = list_entry(stream1.completed_list.next,
271 struct tapdisk_stream_request, next);
272 sreq2 = list_entry(stream2.completed_list.next,
273 struct tapdisk_stream_request, next);
274 tmp1 = list_entry(sreq1->next.next,
275 struct tapdisk_stream_request, next);
276 tmp2 = list_entry(sreq2->next.next,
277 struct tapdisk_stream_request, next);
278 while (result == 0 &&
279 &sreq1->next != &stream1.completed_list &&
280 &sreq2->next != &stream2.completed_list) {
281 //printf("checking: %llu|%llu\n", sreq1->seqno, sreq2->seqno);
282 advance_both = 1;
283 if (sreq1->seqno < sreq2->seqno) {
284 advance_both = 0;
285 goto advance1;
286 }
287 if (sreq1->seqno > sreq2->seqno)
288 goto advance2;
290 result = tapdisk_result_compare(sreq1, sreq2);
292 stream1.completed++;
293 stream2.completed++;
295 list_del_init(&sreq1->next);
296 list_add_tail(&sreq1->next, &stream1.free_list);
297 list_del_init(&sreq2->next);
298 list_add_tail(&sreq2->next, &stream2.free_list);
300 advance1:
301 sreq1 = tmp1;
302 tmp1 = list_entry(tmp1->next.next,
303 struct tapdisk_stream_request, next);
304 if (!advance_both)
305 continue;
306 advance2:
307 sreq2 = tmp2;
308 tmp2 = list_entry(tmp2->next.next,
309 struct tapdisk_stream_request, next);
310 }
312 return result;
313 }
315 static void
316 tapdisk_stream_dequeue(void *arg, blkif_response_t *rsp)
317 {
318 struct tapdisk_stream *s = (struct tapdisk_stream *)arg;
319 struct tapdisk_stream_request *sreq = s->requests + rsp->id;
321 list_del_init(&sreq->next);
323 if (rsp->status == BLKIF_RSP_OKAY)
324 tapdisk_stream_queue_completed(s, sreq);
325 else {
326 s->err = EIO;
327 list_add_tail(&sreq->next, &s->free_list);
328 fprintf(stderr, "error reading sector 0x%"PRIx64"\n", sreq->sec);
329 }
331 if (tapdisk_stream_process_data()) {
332 fprintf(stderr, "mismatch at sector 0x%"PRIx64"\n",
333 sreq->sec);
334 stream1.err = EINVAL;
335 stream2.err = EINVAL;
336 }
338 tapdisk_stream_poll_set(&stream1.poll);
339 tapdisk_stream_poll_set(&stream2.poll);
340 }
342 static inline int
343 tapdisk_stream_enqueue_copy(struct tapdisk_stream *s,
344 struct tapdisk_stream_request *r)
345 {
346 td_vbd_t *vbd;
347 blkif_request_t *breq;
348 td_vbd_request_t *vreq;
349 struct tapdisk_stream_request *sreq;
350 int idx;
352 vbd = stream2.vbd;
353 sreq = tapdisk_stream_get_request(s);
354 if (!sreq)
355 return 1;
357 idx = tapdisk_stream_request_idx(s, sreq);
359 sreq->sec = r->sec;
360 sreq->secs = r->secs;
361 sreq->seqno = r->seqno;
363 breq = &sreq->blkif_req;
364 breq->id = idx;
365 breq->nr_segments = r->blkif_req.nr_segments;
366 breq->sector_number = r->blkif_req.sector_number;
367 breq->operation = BLKIF_OP_READ;
369 for (int i = 0; i < r->blkif_req.nr_segments; i++) {
370 struct blkif_request_segment *seg = breq->seg + i;
371 seg->first_sect = r->blkif_req.seg[i].first_sect;
372 seg->last_sect = r->blkif_req.seg[i].last_sect;
373 }
374 s->cur += sreq->secs;
376 vreq = vbd->request_list + idx;
377 assert(list_empty(&vreq->next));
378 assert(vreq->secs_pending == 0);
380 memcpy(&vreq->req, breq, sizeof(*breq));
381 vbd->received++;
382 vreq->vbd = vbd;
384 tapdisk_vbd_move_request(vreq, &vbd->new_requests);
385 list_add_tail(&sreq->next, &s->pending_list);
387 return 0;
388 }
390 static void
391 tapdisk_stream_enqueue1(void)
392 {
393 td_vbd_t *vbd;
394 int i, idx, psize, blk;
395 struct tapdisk_stream *s = &stream1;
397 vbd = s->vbd;
398 psize = getpagesize();
400 while (s->cur < s->end && !s->err) {
401 blkif_request_t *breq;
402 td_vbd_request_t *vreq;
403 struct tapdisk_stream_request *sreq;
405 /* skip any blocks that are not present in this image */
406 blk = s->cur >> SPB_SHIFT;
407 while (s->cur < s->end && vhd1.bat.bat[blk] == DD_BLK_UNUSED) {
408 //printf("skipping block %d\n", blk);
409 blk++;
410 s->cur = blk << SPB_SHIFT;
411 }
413 if (s->cur >= s->end)
414 break;
416 sreq = tapdisk_stream_get_request(s);
417 if (!sreq)
418 break;
420 idx = tapdisk_stream_request_idx(s, sreq);
422 sreq->sec = s->cur;
423 sreq->secs = 0;
424 sreq->seqno = s->started++;
426 breq = &sreq->blkif_req;
427 breq->id = idx;
428 breq->nr_segments = 0;
429 breq->sector_number = sreq->sec;
430 breq->operation = BLKIF_OP_READ;
432 for (i = 0; i < BLKIF_MAX_SEGMENTS_PER_REQUEST; i++) {
433 uint32_t secs;
434 struct blkif_request_segment *seg = breq->seg + i;
436 secs = MIN(s->end - s->cur, psize >> SECTOR_SHIFT);
437 secs = MIN(((blk + 1) << SPB_SHIFT) - s->cur, secs);
438 if (!secs)
439 break;
441 sreq->secs += secs;
442 s->cur += secs;
444 seg->first_sect = 0;
445 seg->last_sect = secs - 1;
446 breq->nr_segments++;
447 }
449 vreq = vbd->request_list + idx;
451 assert(list_empty(&vreq->next));
452 assert(vreq->secs_pending == 0);
454 memcpy(&vreq->req, breq, sizeof(*breq));
455 vbd->received++;
456 vreq->vbd = vbd;
458 tapdisk_vbd_move_request(vreq, &vbd->new_requests);
459 list_add_tail(&sreq->next, &s->pending_list);
460 }
462 tapdisk_vbd_issue_requests(vbd);
463 }
465 static void
466 tapdisk_stream_enqueue2(void)
467 {
468 td_vbd_t *vbd;
469 int i, blk;
470 struct tapdisk_stream_request *itr;
471 struct tapdisk_stream *s = &stream2;
473 vbd = s->vbd;
475 /* issue the same requests that we issued on stream1 */
476 list_for_each_entry(itr, &stream1.completed_list, next) {
477 if (itr->sec < s->cur)
478 continue;
479 if (tapdisk_stream_enqueue_copy(s, itr))
480 goto done;
481 }
483 list_for_each_entry(itr, &stream1.pending_list, next) {
484 if (itr->sec < s->cur)
485 continue;
486 if (tapdisk_stream_enqueue_copy(s, itr))
487 goto done;
488 }
490 stream2.cur = stream1.cur;
492 done:
493 tapdisk_vbd_issue_requests(vbd);
494 }
496 static inline int
497 tapdisk_diff_done(void)
498 {
499 return (tapdisk_stream_stop(&stream1) && tapdisk_stream_stop(&stream2));
500 }
502 static void
503 tapdisk_diff_stop(void)
504 {
505 tapdisk_stream_close_image(&stream1);
506 tapdisk_stream_close_image(&stream2);
507 }
509 static void
510 tapdisk_stream_enqueue(event_id_t id, char mode, void *arg)
511 {
512 struct tapdisk_stream *s = (struct tapdisk_stream *)arg;
514 tapdisk_stream_poll_clear(&s->poll);
516 if (tapdisk_diff_done()) {
517 tapdisk_diff_stop();
518 return;
519 }
521 if (s == &stream1)
522 tapdisk_stream_enqueue1();
523 else if (s == &stream2)
524 tapdisk_stream_enqueue2();
525 else
526 assert(0);
528 if (tapdisk_diff_done()) {
529 // we have to check again for the case when stream1 had no
530 // blocks at all
531 tapdisk_diff_stop();
532 return;
533 }
534 }
536 static int
537 tapdisk_stream_open_image(struct tapdisk_stream *s, const char *path, int type)
538 {
539 int err;
540 image_t image;
542 s->id = tapdisk_stream_count++;
544 err = tapdisk_vbd_initialize(s->id);
545 if (err)
546 goto out;
548 s->vbd = tapdisk_server_get_vbd(s->id);
549 if (!s->vbd) {
550 err = ENODEV;
551 goto out;
552 }
554 tapdisk_vbd_set_callback(s->vbd, tapdisk_stream_dequeue, s);
556 err = tapdisk_vbd_open_vdi(s->vbd, path, type,
557 TAPDISK_STORAGE_TYPE_DEFAULT,
558 TD_OPEN_RDONLY);
559 if (err)
560 goto out;
562 s->vbd->reopened = 1;
564 err = tapdisk_vbd_get_image_info(s->vbd, &image);
565 if (err) {
566 fprintf(stderr, "failed getting image size: %d\n", err);
567 return err;
568 }
570 s->start = 0;
571 s->cur = s->start;
572 s->end = image.size;
574 err = 0;
576 out:
577 if (err)
578 fprintf(stderr, "failed to open image %s: %d\n", path, err);
579 return err;
580 }
582 static void
583 tapdisk_stream_close_image(struct tapdisk_stream *s)
584 {
585 td_vbd_t *vbd;
587 vbd = tapdisk_server_get_vbd(s->id);
588 if (vbd) {
589 tapdisk_vbd_close_vdi(vbd);
590 tapdisk_server_remove_vbd(vbd);
591 free((void *)vbd->ring.vstart);
592 free(vbd->name);
593 free(vbd);
594 s->vbd = NULL;
595 }
596 }
598 static int
599 tapdisk_stream_initialize_requests(struct tapdisk_stream *s)
600 {
601 size_t size;
602 td_ring_t *ring;
603 int err, i, psize;
605 ring = &s->vbd->ring;
606 psize = getpagesize();
607 size = psize * BLKTAP_MMAP_REGION_SIZE;
609 /* sneaky -- set up ring->vstart so tapdisk_vbd will use our buffers */
610 err = posix_memalign((void **)&ring->vstart, psize, size);
611 if (err) {
612 fprintf(stderr, "failed to allocate buffers: %d\n", err);
613 ring->vstart = 0;
614 return err;
615 }
617 for (i = 0; i < MAX_REQUESTS; i++) {
618 struct tapdisk_stream_request *req = s->requests + i;
619 tapdisk_stream_initialize_request(req);
620 list_add_tail(&req->next, &s->free_list);
621 }
623 return 0;
624 }
626 static int
627 tapdisk_stream_register_enqueue_event(struct tapdisk_stream *s)
628 {
629 int err;
630 struct tapdisk_stream_poll *p = &s->poll;
632 err = tapdisk_stream_poll_open(p);
633 if (err)
634 goto out;
636 err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
637 p->pipe[POLL_READ], 0,
638 tapdisk_stream_enqueue, s);
639 if (err < 0)
640 goto out;
642 s->enqueue_event_id = err;
643 err = 0;
645 out:
646 if (err)
647 fprintf(stderr, "failed to register event: %d\n", err);
648 return err;
649 }
651 static void
652 tapdisk_stream_unregister_enqueue_event(struct tapdisk_stream *s)
653 {
654 if (s->enqueue_event_id) {
655 tapdisk_server_unregister_event(s->enqueue_event_id);
656 s->enqueue_event_id = 0;
657 }
658 tapdisk_stream_poll_close(&s->poll);
659 }
661 static inline void
662 tapdisk_stream_initialize(struct tapdisk_stream *s)
663 {
664 memset(s, 0, sizeof(*s));
665 INIT_LIST_HEAD(&s->free_list);
666 INIT_LIST_HEAD(&s->pending_list);
667 INIT_LIST_HEAD(&s->completed_list);
668 }
670 static int
671 tapdisk_stream_open(struct tapdisk_stream *s, const char *arg)
672 {
673 int err, type;
674 const char *path;
676 type = tapdisk_disktype_parse_params(arg, &path);
677 if (type < 0)
678 return type;
680 tapdisk_stream_initialize(s);
682 err = tapdisk_stream_open_image(s, path, type);
683 if (err)
684 return err;
686 err = tapdisk_stream_initialize_requests(s);
687 if (err)
688 return err;
690 err = tapdisk_stream_register_enqueue_event(s);
691 if (err)
692 return err;
694 tapdisk_stream_enqueue(s->enqueue_event_id,
695 SCHEDULER_POLL_READ_FD, s);
697 return 0;
698 }
700 static void
701 tapdisk_stream_release(struct tapdisk_stream *s)
702 {
703 tapdisk_stream_close_image(s);
704 tapdisk_stream_unregister_enqueue_event(s);
705 }
707 static int
708 tapdisk_stream_run(struct tapdisk_stream *s)
709 {
710 tapdisk_stream_enqueue(s->enqueue_event_id, SCHEDULER_POLL_READ_FD, s);
711 tapdisk_server_run();
712 return s->err;
713 }
715 int
716 main(int argc, char *argv[])
717 {
718 int c, err, type1;
719 const char *arg1 = NULL, *arg2 = NULL;
720 const disk_info_t *info;
721 const char *path1;
723 err = 0;
725 program = basename(argv[0]);
727 while ((c = getopt(argc, argv, "n:m:h")) != -1) {
728 switch (c) {
729 case 'n':
730 arg1 = optarg;
731 break;
732 case 'm':
733 arg2 = optarg;
734 break;
735 case 'h':
736 usage(stdout);
737 return 0;
738 default:
739 goto fail_usage;
740 }
741 }
743 if (!arg1 || !arg2)
744 goto fail_usage;
746 type1 = tapdisk_disktype_parse_params(arg1, &path1);
747 if (type1 < 0)
748 return type1;
750 if (type1 != DISK_TYPE_VHD) {
751 printf("error: first VDI is not VHD\n");
752 return EINVAL;
753 }
755 err = open_vhd(path1, &vhd1);
756 if (err)
757 return err;
759 tapdisk_start_logging("tapdisk-diff");
761 err = tapdisk_server_initialize();
762 if (err)
763 goto out;
765 err = tapdisk_stream_open(&stream1, arg1);
766 if (err) {
767 fprintf(stderr, "Failed to open %s: %s\n",
768 arg1, strerror(-err));
769 goto out;
770 }
772 err = tapdisk_stream_open(&stream2, arg2);
773 if (err) {
774 fprintf(stderr, "Failed to open %s: %s\n",
775 arg2, strerror(-err));
776 goto out1;
777 }
779 if (stream1.end != stream2.end) {
780 fprintf(stderr, "Image sizes differ: %"PRIu64" != %"PRIu64"\n",
781 stream1.end, stream2.end);
782 err = EINVAL;
783 goto out2;
784 }
786 tapdisk_server_run();
788 out2:
789 tapdisk_stream_release(&stream2);
790 out1:
791 tapdisk_stream_release(&stream1);
792 out:
793 vhd_close(&vhd1);
794 tapdisk_stop_logging();
796 return err ? : stream1.err;
798 fail_usage:
799 usage(stderr);
800 return 1;
801 }