Statistics
| Branch: | Revision:

root / drivers / tapdisk-diff.c @ abdb293f

History | View | Annotate | Download (18.3 kB)

1
/*
2
 * Copyright (c) 2009, Citrix Systems, Inc.
3
 *
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *     * Redistributions of source code must retain the above copyright
9
 *       notice, this list of conditions and the following disclaimer.
10
 *     * Redistributions in binary form must reproduce the above copyright
11
 *       notice, this list of conditions and the following disclaimer in the
12
 *       documentation and/or other materials provided with the distribution.
13
 *     * Neither the name of XenSource Inc. nor the names of its contributors
14
 *       may be used to endorse or promote products derived from this software
15
 *       without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29

    
30
#ifdef HAVE_CONFIG_H
31
#include "config.h"
32
#endif
33

    
34
#include <stdio.h>
35
#include <errno.h>
36
#include <fcntl.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include <assert.h>
40
#include <unistd.h>
41

    
42
#include "list.h"
43
#include "scheduler.h"
44
#include "tapdisk-vbd.h"
45
#include "tapdisk-server.h"
46
#include "tapdisk-disktype.h"
47
#include "libvhd.h"
48

    
49
#define POLL_READ                        0
50
#define POLL_WRITE                       1
51

    
52
#define SPB_SHIFT (VHD_BLOCK_SHIFT - SECTOR_SHIFT)
53

    
54
/* 
55
 * we have to use half the max number of requests because we're using the same 
56
 * tapdisk server for both streams and all the parents will be shared. If we 
57
 * issue more than MAX_REQUESTS/2 requests, the vhd_state will run out of 
58
 * vhd_request's and return EBUSY, which we don't handle here. However, even 
59
 * with MAX_REQUESTS/2 we can still run out of vhd_request's because of 
60
 * splitting: if some sectors spanned by a segment are in a parent, a segment 
61
 * could be split into at most N/2 vhd_request's, where N is the number of 
62
 * sectors per segment. Therefore, if we use 11 segments, we need to divide 
63
 * MAX_REQUESTS by 11/2=6 on top of that. If we don't, we'd have to handle 
64
 * EBUSY by retrying here.
65
 */
66
#define MAX_SEGMENTS 8
67
#define MAX_STREAM_REQUESTS (MAX_REQUESTS / 2 / (MAX_SEGMENTS / 2))
68

    
69
struct tapdisk_stream_poll {
70
        int                              pipe[2];
71
        int                              set;
72
};
73

    
74
struct tapdisk_stream_request {
75
        uint64_t                         sec;
76
        uint32_t                         secs;
77
        uint64_t                         seqno;
78
        blkif_request_t                  blkif_req;
79
        struct list_head                 next;
80
};
81

    
82
struct tapdisk_stream {
83
        td_vbd_t                        *vbd;
84

    
85
        unsigned int                     id;
86

    
87
        int                              err;
88

    
89
        uint64_t                         cur;
90
        uint64_t                         start;
91
        uint64_t                         end;
92

    
93
        uint64_t                         started;
94
        uint64_t                         completed;
95

    
96
        struct tapdisk_stream_poll       poll;
97
        event_id_t                       enqueue_event_id;
98

    
99
        struct list_head                 free_list;
100
        struct list_head                 pending_list;
101
        struct list_head                 completed_list;
102

    
103
        struct tapdisk_stream_request    requests[MAX_STREAM_REQUESTS];
104
};
105

    
106
static unsigned int tapdisk_stream_count;
107

    
108
static void tapdisk_stream_close_image(struct tapdisk_stream *);
109

    
110
static char *program;
111
static struct tapdisk_stream stream1, stream2;
112
static vhd_context_t vhd1;
113

    
114
static void
115
usage(FILE *stream)
116
{
117
        printf("usage: %s <-n type:/path/to/image> <-m type:/path/to/image>\n",
118
                        program);
119
}
120

    
121
static int
122
open_vhd(const char *path, vhd_context_t *vhd)
123
{
124
        int err;
125

    
126
        err = vhd_open(vhd, path, VHD_OPEN_RDONLY);
127
        if (err) {
128
                printf("error opening %s: %d\n", path, err);
129
                return err;
130
        }
131

    
132
        err = vhd_get_bat(vhd);
133
        if (err)
134
        {
135
                printf("error reading BAT for %s: %d\n", path, err);
136
                vhd_close(vhd);
137
                return err;
138
        }
139

    
140
        return 0;
141
}
142

    
143
static inline void
144
tapdisk_stream_poll_initialize(struct tapdisk_stream_poll *p)
145
{
146
        p->set = 0;
147
        p->pipe[POLL_READ] = p->pipe[POLL_WRITE] = -1;
148
}
149

    
150
static int
151
tapdisk_stream_poll_open(struct tapdisk_stream_poll *p)
152
{
153
        int err;
154

    
155
        tapdisk_stream_poll_initialize(p);
156

    
157
        err = pipe(p->pipe);
158
        if (err)
159
                return -errno;
160

    
161
        err = fcntl(p->pipe[POLL_READ], F_SETFL, O_NONBLOCK);
162
        if (err)
163
                goto out;
164

    
165
        err = fcntl(p->pipe[POLL_WRITE], F_SETFL, O_NONBLOCK);
166
        if (err)
167
                goto out;
168

    
169
        return 0;
170

    
171
out:
172
        close(p->pipe[POLL_READ]);
173
        close(p->pipe[POLL_WRITE]);
174
        tapdisk_stream_poll_initialize(p);
175
        return -errno;
176
}
177

    
178
static void
179
tapdisk_stream_poll_close(struct tapdisk_stream_poll *p)
180
{
181
        if (p->pipe[POLL_READ] != -1)
182
                close(p->pipe[POLL_READ]);
183
        if (p->pipe[POLL_WRITE] != -1)
184
                close(p->pipe[POLL_WRITE]);
185
        tapdisk_stream_poll_initialize(p);
186
}
187

    
188
static inline void
189
tapdisk_stream_poll_clear(struct tapdisk_stream_poll *p)
190
{
191
        int gcc, dummy;
192

    
193
        gcc = read(p->pipe[POLL_READ], &dummy, sizeof(dummy));
194
        p->set = 0;
195
}
196

    
197
static inline void
198
tapdisk_stream_poll_set(struct tapdisk_stream_poll *p)
199
{
200
        int dummy = 0;
201

    
202
        if (!p->set) {
203
                int gcc = write(p->pipe[POLL_WRITE], &dummy, sizeof(dummy));
204
                p->set = 1;
205
        }
206
}
207

    
208
static inline int
209
tapdisk_stream_stop(struct tapdisk_stream *s)
210
{
211
        return ((s->cur == s->end || s->err) &&
212
                        list_empty(&s->pending_list) && 
213
                        list_empty(&s->completed_list));
214
}
215

    
216
static inline void
217
tapdisk_stream_initialize_request(struct tapdisk_stream_request *req)
218
{
219
        memset(req, 0, sizeof(*req));
220
        INIT_LIST_HEAD(&req->next);
221
}
222

    
223
static inline int
224
tapdisk_stream_request_idx(struct tapdisk_stream *s,
225
                           struct tapdisk_stream_request *req)
226
{
227
        return (req - s->requests);
228
}
229

    
230
static inline struct tapdisk_stream_request *
231
tapdisk_stream_get_request(struct tapdisk_stream *s)
232
{
233
        struct tapdisk_stream_request *req;
234

    
235
        if (list_empty(&s->free_list))
236
                return NULL;
237

    
238
        req = list_entry(s->free_list.next,
239
                         struct tapdisk_stream_request, next);
240

    
241
        list_del_init(&req->next);
242
        tapdisk_stream_initialize_request(req);
243

    
244
        return req;
245
}
246

    
247
static inline void
248
tapdisk_stream_queue_completed(struct tapdisk_stream *s,
249
                               struct tapdisk_stream_request *sreq)
250
{
251
        struct tapdisk_stream_request *itr;
252

    
253
        list_for_each_entry(itr, &s->completed_list, next)
254
                if (sreq->seqno < itr->seqno) {
255
                        list_add_tail(&sreq->next, &itr->next);
256
                        return;
257
                }
258

    
259
        list_add_tail(&sreq->next, &s->completed_list);
260
}
261

    
262
static int 
263
tapdisk_result_compare(struct tapdisk_stream_request *sreq1,
264
                struct tapdisk_stream_request  *sreq2)
265
{
266
        unsigned long idx1, idx2;
267
        char *buf1, *buf2;
268
        int result;
269

    
270
        assert(sreq1->seqno == sreq2->seqno);
271
        assert(sreq1->secs == sreq2->secs);
272
        idx1 = (unsigned long)tapdisk_stream_request_idx(&stream1, 
273
                        sreq1);
274
        idx2 = (unsigned long)tapdisk_stream_request_idx(&stream2,
275
                        sreq2);
276
        buf1 = (char *)MMAP_VADDR(stream1.vbd->ring.vstart, idx1, 0);
277
        buf2 = (char *)MMAP_VADDR(stream2.vbd->ring.vstart, idx2, 0);
278

    
279
        result = memcmp(buf1, buf2, sreq1->secs << SECTOR_SHIFT);
280
        return result;
281
}
282

    
283
static int
284
tapdisk_stream_process_data(void)
285
{
286
        struct tapdisk_stream_request *sreq1, *sreq2, *tmp1, *tmp2;
287
        int advance_both;
288
        int result = 0;
289

    
290
        sreq1 = list_entry(stream1.completed_list.next,
291
                        struct tapdisk_stream_request, next);
292
        sreq2 = list_entry(stream2.completed_list.next,
293
                        struct tapdisk_stream_request, next);
294
        tmp1 = list_entry(sreq1->next.next,
295
                        struct tapdisk_stream_request, next);
296
        tmp2 = list_entry(sreq2->next.next,
297
                        struct tapdisk_stream_request, next);
298
        while (result == 0 &&
299
                        &sreq1->next != &stream1.completed_list &&
300
                        &sreq2->next != &stream2.completed_list) {
301
                //printf("checking: %llu|%llu\n", sreq1->seqno, sreq2->seqno);
302
                advance_both = 1;
303
                if (sreq1->seqno < sreq2->seqno) {
304
                        advance_both = 0;
305
                        goto advance1;
306
                }
307
                if (sreq1->seqno > sreq2->seqno)
308
                        goto advance2;
309

    
310
                result = tapdisk_result_compare(sreq1, sreq2);
311

    
312
                stream1.completed++;
313
                stream2.completed++;
314
                
315
                list_del_init(&sreq1->next);
316
                list_add_tail(&sreq1->next, &stream1.free_list);
317
                list_del_init(&sreq2->next);
318
                list_add_tail(&sreq2->next, &stream2.free_list);
319

    
320
advance1:
321
                sreq1 = tmp1;
322
                tmp1 = list_entry(tmp1->next.next, 
323
                                struct tapdisk_stream_request, next);
324
                if (!advance_both)
325
                        continue;
326
advance2:
327
                sreq2 = tmp2;
328
                tmp2 = list_entry(tmp2->next.next, 
329
                                struct tapdisk_stream_request, next);
330
        }
331

    
332
        return result;
333
}
334

    
335
static void
336
tapdisk_stream_dequeue(void *arg, blkif_response_t *rsp)
337
{
338
        struct tapdisk_stream *s = (struct tapdisk_stream *)arg;
339
        struct tapdisk_stream_request *sreq = s->requests + rsp->id;
340

    
341
        list_del_init(&sreq->next);
342

    
343
        if (rsp->status == BLKIF_RSP_OKAY)
344
                tapdisk_stream_queue_completed(s, sreq);
345
        else {
346
                s->err = EIO;
347
                list_add_tail(&sreq->next, &s->free_list);
348
                fprintf(stderr, "error reading sector %llu (stream %d)\n",
349
                                sreq->sec, (s == &stream2) + 1);
350
        }
351

    
352
        if (tapdisk_stream_process_data()) {
353
                fprintf(stderr, "mismatch at sector %llu\n",
354
                                sreq->sec);
355
                stream1.err = EINVAL;
356
                stream2.err = EINVAL;
357
        }
358

    
359
        tapdisk_stream_poll_set(&stream1.poll);
360
        tapdisk_stream_poll_set(&stream2.poll);
361
}
362

    
363
static inline int
364
tapdisk_stream_enqueue_copy(struct tapdisk_stream *s, 
365
                struct tapdisk_stream_request *r)
366
{
367
        td_vbd_t *vbd;
368
        blkif_request_t *breq;
369
        td_vbd_request_t *vreq;
370
        struct tapdisk_stream_request *sreq;
371
        int idx;
372

    
373
        vbd = stream2.vbd;
374
        sreq = tapdisk_stream_get_request(s);
375
        if (!sreq)
376
                return 1;
377

    
378
        idx                 = tapdisk_stream_request_idx(s, sreq);
379

    
380
        sreq->sec           = r->sec;
381
        sreq->secs          = r->secs;
382
        sreq->seqno         = r->seqno;
383

    
384
        breq                = &sreq->blkif_req;
385
        breq->id            = idx;
386
        breq->nr_segments   = r->blkif_req.nr_segments;
387
        breq->sector_number = r->blkif_req.sector_number;
388
        breq->operation     = BLKIF_OP_READ;
389

    
390
        for (int i = 0; i < r->blkif_req.nr_segments; i++) {
391
                struct blkif_request_segment *seg = breq->seg + i;
392
                seg->first_sect = r->blkif_req.seg[i].first_sect;
393
                seg->last_sect  = r->blkif_req.seg[i].last_sect;
394
        }
395
        s->cur += sreq->secs;
396

    
397
        vreq = vbd->request_list + idx;
398
        assert(list_empty(&vreq->next));
399
        assert(vreq->secs_pending == 0);
400

    
401
        memcpy(&vreq->req, breq, sizeof(*breq));
402
        s->started++;
403
        vbd->received++;
404
        vreq->vbd = vbd;
405

    
406
        tapdisk_vbd_move_request(vreq, &vbd->new_requests);
407
        list_add_tail(&sreq->next, &s->pending_list);
408

    
409
        return 0;
410
}
411

    
412
static void
413
tapdisk_stream_enqueue1(void)
414
{
415
        td_vbd_t *vbd;
416
        int i, idx, psize, blk;
417
        struct tapdisk_stream *s = &stream1;
418

    
419
        vbd = s->vbd;
420
        psize = getpagesize();
421

    
422
        while (s->cur < s->end && !s->err) {
423
                blkif_request_t *breq;
424
                td_vbd_request_t *vreq;
425
                struct tapdisk_stream_request *sreq;
426

    
427
                /* skip any blocks that are not present in this image */
428
                blk = s->cur >> SPB_SHIFT;
429
                while (s->cur < s->end && vhd1.bat.bat[blk] == DD_BLK_UNUSED) {
430
                        //printf("skipping block %d\n", blk);
431
                        blk++;
432
                        s->cur = blk << SPB_SHIFT;
433
                }
434

    
435
                if (s->cur >= s->end)
436
                        break;
437

    
438
                sreq = tapdisk_stream_get_request(s);
439
                if (!sreq)
440
                        break;
441

    
442
                idx                 = tapdisk_stream_request_idx(s, sreq);
443

    
444
                sreq->sec           = s->cur;
445
                sreq->secs          = 0;
446
                sreq->seqno         = s->started++;
447

    
448
                breq                = &sreq->blkif_req;
449
                breq->id            = idx;
450
                breq->nr_segments   = 0;
451
                breq->sector_number = sreq->sec;
452
                breq->operation     = BLKIF_OP_READ;
453

    
454
                for (i = 0; i < MAX_SEGMENTS; i++) {
455
                        uint32_t secs;
456
                        struct blkif_request_segment *seg = breq->seg + i;
457

    
458
                        secs = MIN(s->end - s->cur, psize >> SECTOR_SHIFT);
459
                        secs = MIN(((blk + 1) << SPB_SHIFT) - s->cur, secs);
460
                        if (!secs)
461
                                break;
462

    
463
                        sreq->secs += secs;
464
                        s->cur     += secs;
465

    
466
                        seg->first_sect = 0;
467
                        seg->last_sect  = secs - 1;
468
                        breq->nr_segments++;
469
                }
470

    
471
                vreq = vbd->request_list + idx;
472

    
473
                assert(list_empty(&vreq->next));
474
                assert(vreq->secs_pending == 0);
475

    
476
                memcpy(&vreq->req, breq, sizeof(*breq));
477
                vbd->received++;
478
                vreq->vbd = vbd;
479

    
480
                tapdisk_vbd_move_request(vreq, &vbd->new_requests);
481
                list_add_tail(&sreq->next, &s->pending_list);
482
        }
483

    
484
        tapdisk_vbd_issue_requests(vbd);
485
}
486

    
487
static void
488
tapdisk_stream_enqueue2(void)
489
{
490
        td_vbd_t *vbd;
491
        int i, blk;
492
        struct tapdisk_stream_request *itr;
493
        struct tapdisk_stream *s = &stream2;
494

    
495
        vbd = s->vbd;
496

    
497
        /* issue the same requests that we issued on stream1 */
498
        list_for_each_entry(itr, &stream1.completed_list, next) {
499
                if (itr->sec < s->cur)
500
                        continue;
501
                if (tapdisk_stream_enqueue_copy(s, itr))
502
                        goto done;
503
        }
504

    
505
        list_for_each_entry(itr, &stream1.pending_list, next) {
506
                if (itr->sec < s->cur)
507
                        continue;
508
                if (tapdisk_stream_enqueue_copy(s, itr))
509
                        goto done;
510
        }
511

    
512
        stream2.cur = stream1.cur;
513

    
514
done:
515
        tapdisk_vbd_issue_requests(vbd);
516
}
517

    
518
static inline int
519
tapdisk_diff_done(void)
520
{
521
        return (tapdisk_stream_stop(&stream1) && tapdisk_stream_stop(&stream2));
522
}
523

    
524
static void
525
tapdisk_diff_stop(void)
526
{
527
        tapdisk_stream_close_image(&stream1);
528
        tapdisk_stream_close_image(&stream2);
529
}
530

    
531
static void
532
tapdisk_stream_enqueue(event_id_t id, char mode, void *arg)
533
{
534
        struct tapdisk_stream *s = (struct tapdisk_stream *)arg;
535

    
536
        tapdisk_stream_poll_clear(&s->poll);
537

    
538
        if (tapdisk_diff_done()) {
539
                tapdisk_diff_stop();
540
                return;
541
        }
542

    
543
        if (s == &stream1) 
544
                tapdisk_stream_enqueue1();
545
        else if (s == &stream2)
546
                tapdisk_stream_enqueue2();
547
        else
548
                assert(0);
549

    
550
        if (tapdisk_diff_done()) {
551
                // we have to check again for the case when stream1 had no 
552
                // blocks at all
553
                tapdisk_diff_stop();
554
                return;
555
        }
556
}
557

    
558
static int
559
tapdisk_stream_open_image(struct tapdisk_stream *s, const char *name)
560
{
561
        int err;
562
        td_disk_info_t info;
563

    
564
        s->id = tapdisk_stream_count++;
565

    
566
        err = tapdisk_vbd_initialize(-1, -1, s->id);
567
        if (err)
568
                goto out;
569

    
570
        s->vbd = tapdisk_server_get_vbd(s->id);
571
        if (!s->vbd) {
572
                err = ENODEV;
573
                goto out;
574
        }
575

    
576
        tapdisk_vbd_set_callback(s->vbd, tapdisk_stream_dequeue, s);
577

    
578
        err = tapdisk_vbd_open_vdi(s->vbd, name, TD_OPEN_RDONLY, -1);
579
        if (err)
580
                goto out;
581

    
582
        err = tapdisk_vbd_get_disk_info(s->vbd, &info);
583
        if (err) {
584
                fprintf(stderr, "failed getting image size: %d\n", err);
585
                return err;
586
        }
587

    
588
        s->start = 0;
589
        s->cur   = s->start;
590
        s->end   = info.size;
591

    
592
        err = 0;
593

    
594
out:
595
        if (err)
596
                fprintf(stderr, "failed to open image %s: %d\n", name, err);
597
        return err;
598
}
599

    
600
static void
601
tapdisk_stream_close_image(struct tapdisk_stream *s)
602
{
603
        td_vbd_t *vbd;
604

    
605
        vbd = tapdisk_server_get_vbd(s->id);
606
        if (vbd) {
607
                tapdisk_vbd_close_vdi(vbd);
608
                tapdisk_server_remove_vbd(vbd);
609
                free((void *)vbd->ring.vstart);
610
                free(vbd->name);
611
                free(vbd);
612
                s->vbd = NULL;
613
        }
614
}
615

    
616
static int
617
tapdisk_stream_initialize_requests(struct tapdisk_stream *s)
618
{
619
        size_t size;
620
        td_ring_t *ring;
621
        int err, i, psize;
622

    
623
        ring  = &s->vbd->ring;
624
        psize = getpagesize();
625
        size  = psize * BLKTAP_MMAP_REGION_SIZE;
626

    
627
        /* sneaky -- set up ring->vstart so tapdisk_vbd will use our buffers */
628
        err = posix_memalign((void **)&ring->vstart, psize, size);
629
        if (err) {
630
                fprintf(stderr, "failed to allocate buffers: %d\n", err);
631
                ring->vstart = 0;
632
                return err;
633
        }
634

    
635
        for (i = 0; i < MAX_STREAM_REQUESTS; i++) {
636
                struct tapdisk_stream_request *req = s->requests + i;
637
                tapdisk_stream_initialize_request(req);
638
                list_add_tail(&req->next, &s->free_list);
639
        }
640

    
641
        return 0;
642
}
643

    
644
static int
645
tapdisk_stream_register_enqueue_event(struct tapdisk_stream *s)
646
{
647
        int err;
648
        struct tapdisk_stream_poll *p = &s->poll;
649

    
650
        err = tapdisk_stream_poll_open(p);
651
        if (err)
652
                goto out;
653

    
654
        err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
655
                                            p->pipe[POLL_READ], 0,
656
                                            tapdisk_stream_enqueue, s);
657
        if (err < 0)
658
                goto out;
659

    
660
        s->enqueue_event_id = err;
661
        err = 0;
662

    
663
out:
664
        if (err)
665
                fprintf(stderr, "failed to register event: %d\n", err);
666
        return err;
667
}
668

    
669
static void
670
tapdisk_stream_unregister_enqueue_event(struct tapdisk_stream *s)
671
{
672
        if (s->enqueue_event_id) {
673
                tapdisk_server_unregister_event(s->enqueue_event_id);
674
                s->enqueue_event_id = 0;
675
        }
676
        tapdisk_stream_poll_close(&s->poll);
677
}
678

    
679
static inline void
680
tapdisk_stream_initialize(struct tapdisk_stream *s)
681
{
682
        memset(s, 0, sizeof(*s));
683
        INIT_LIST_HEAD(&s->free_list);
684
        INIT_LIST_HEAD(&s->pending_list);
685
        INIT_LIST_HEAD(&s->completed_list);
686
}
687

    
688
static int
689
tapdisk_stream_open(struct tapdisk_stream *s, const char *arg)
690
{
691
        int err;
692

    
693
        tapdisk_stream_initialize(s);
694

    
695
        err = tapdisk_stream_open_image(s, arg);
696
        if (err)
697
                return err;
698

    
699
        err = tapdisk_stream_initialize_requests(s);
700
        if (err)
701
                return err;
702

    
703
        err = tapdisk_stream_register_enqueue_event(s);
704
        if (err)
705
                return err;
706

    
707
        tapdisk_stream_enqueue(s->enqueue_event_id, 
708
                               SCHEDULER_POLL_READ_FD, s);
709

    
710
        return 0;
711
}
712

    
713
static void
714
tapdisk_stream_release(struct tapdisk_stream *s)
715
{
716
        tapdisk_stream_close_image(s);
717
        tapdisk_stream_unregister_enqueue_event(s);
718
}
719

    
720
static int
721
tapdisk_stream_run(struct tapdisk_stream *s)
722
{
723
        tapdisk_stream_enqueue(s->enqueue_event_id, SCHEDULER_POLL_READ_FD, s);
724
        tapdisk_server_run();
725
        return s->err;
726
}
727

    
728
int
729
main(int argc, char *argv[])
730
{
731
        int c, err, type1;
732
        const char *arg1 = NULL, *arg2 = NULL;
733
        const disk_info_t *info;
734
        const char *path1;
735

    
736
        err    = 0;
737

    
738
        program = basename(argv[0]);
739
        
740
        while ((c = getopt(argc, argv, "n:m:h")) != -1) {
741
                switch (c) {
742
                case 'n':
743
                        arg1 = optarg;
744
                        break;
745
                case 'm':
746
                        arg2 = optarg;
747
                        break;
748
                case 'h':
749
                        usage(stdout);
750
                        return 0;
751
                default:
752
                        goto fail_usage;
753
                }
754
        }
755

    
756
        if (!arg1 || !arg2)
757
                goto fail_usage;
758

    
759
        type1 = tapdisk_disktype_parse_params(arg1, &path1);
760
        if (type1 < 0)
761
                return type1;
762

    
763
        if (type1 != DISK_TYPE_VHD) {
764
                printf("error: first VDI is not VHD\n");
765
                return EINVAL;
766
        }
767

    
768
        err = open_vhd(path1, &vhd1);
769
        if (err)
770
                return err;
771

    
772
        tapdisk_start_logging("tapdisk-diff", "daemon");
773

    
774
        err = tapdisk_server_initialize(NULL, NULL);
775
        if (err)
776
                goto out;
777

    
778
        err = tapdisk_stream_open(&stream1, arg1);
779
        if (err) {
780
                fprintf(stderr, "Failed to open %s: %s\n", 
781
                        arg1, strerror(-err));
782
                goto out;
783
        }
784

    
785
        err = tapdisk_stream_open(&stream2, arg2);
786
        if (err) {
787
                fprintf(stderr, "Failed to open %s: %s\n", 
788
                        arg2, strerror(-err));
789
                goto out1;
790
        }
791

    
792
        if (stream1.end != stream2.end) {
793
                fprintf(stderr, "Image sizes differ: %"PRIu64" != %"PRIu64"\n",
794
                                stream1.end, stream2.end);
795
                err = EINVAL;
796
                goto out2;
797
        }
798

    
799
        tapdisk_server_run();
800
        
801
out2:
802
        tapdisk_stream_release(&stream2);
803
out1:
804
        tapdisk_stream_release(&stream1);
805
out:
806
        vhd_close(&vhd1);
807
        tapdisk_stop_logging();
808

    
809
        return err ? : stream1.err;
810

    
811
fail_usage:
812
        usage(stderr);
813
        return 1;
814
}