debuggers.hg

view tools/blktap2/drivers/tapdisk-stream.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 <unistd.h>
36 #include "list.h"
37 #include "scheduler.h"
38 #include "tapdisk-vbd.h"
39 #include "tapdisk-server.h"
40 #include "tapdisk-disktype.h"
42 #define POLL_READ 0
43 #define POLL_WRITE 1
45 #define MIN(a, b) ((a) < (b) ? (a) : (b))
47 struct tapdisk_stream_poll {
48 int pipe[2];
49 int set;
50 };
52 struct tapdisk_stream_request {
53 uint64_t sec;
54 uint32_t secs;
55 uint64_t seqno;
56 blkif_request_t blkif_req;
57 struct list_head next;
58 };
60 struct tapdisk_stream {
61 td_vbd_t *vbd;
63 unsigned int id;
64 int in_fd;
65 int out_fd;
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 void
91 usage(const char *app, int err)
92 {
93 printf("usage: %s <-n type:/path/to/image> "
94 "[-c sector count] [-s skip sectors]\n", app);
95 exit(err);
96 }
98 static inline void
99 tapdisk_stream_poll_initialize(struct tapdisk_stream_poll *p)
100 {
101 p->set = 0;
102 p->pipe[POLL_READ] = p->pipe[POLL_WRITE] = -1;
103 }
105 static int
106 tapdisk_stream_poll_open(struct tapdisk_stream_poll *p)
107 {
108 int err;
110 tapdisk_stream_poll_initialize(p);
112 err = pipe(p->pipe);
113 if (err)
114 return -errno;
116 err = fcntl(p->pipe[POLL_READ], F_SETFL, O_NONBLOCK);
117 if (err)
118 goto out;
120 err = fcntl(p->pipe[POLL_WRITE], F_SETFL, O_NONBLOCK);
121 if (err)
122 goto out;
124 return 0;
126 out:
127 close(p->pipe[POLL_READ]);
128 close(p->pipe[POLL_WRITE]);
129 tapdisk_stream_poll_initialize(p);
130 return -errno;
131 }
133 static void
134 tapdisk_stream_poll_close(struct tapdisk_stream_poll *p)
135 {
136 if (p->pipe[POLL_READ] != -1)
137 close(p->pipe[POLL_READ]);
138 if (p->pipe[POLL_WRITE] != -1)
139 close(p->pipe[POLL_WRITE]);
140 tapdisk_stream_poll_initialize(p);
141 }
143 static inline void
144 tapdisk_stream_poll_clear(struct tapdisk_stream_poll *p)
145 {
146 int dummy;
148 read(p->pipe[POLL_READ], &dummy, sizeof(dummy));
149 p->set = 0;
150 }
152 static inline void
153 tapdisk_stream_poll_set(struct tapdisk_stream_poll *p)
154 {
155 int dummy = 0;
157 if (!p->set) {
158 write(p->pipe[POLL_WRITE], &dummy, sizeof(dummy));
159 p->set = 1;
160 }
161 }
163 static inline int
164 tapdisk_stream_stop(struct tapdisk_stream *s)
165 {
166 return (list_empty(&s->pending_list) && (s->cur == s->end || s->err));
167 }
169 static inline void
170 tapdisk_stream_initialize_request(struct tapdisk_stream_request *req)
171 {
172 memset(req, 0, sizeof(*req));
173 INIT_LIST_HEAD(&req->next);
174 }
176 static inline int
177 tapdisk_stream_request_idx(struct tapdisk_stream *s,
178 struct tapdisk_stream_request *req)
179 {
180 return (req - s->requests);
181 }
183 static inline struct tapdisk_stream_request *
184 tapdisk_stream_get_request(struct tapdisk_stream *s)
185 {
186 struct tapdisk_stream_request *req;
188 if (list_empty(&s->free_list))
189 return NULL;
191 req = list_entry(s->free_list.next,
192 struct tapdisk_stream_request, next);
194 list_del_init(&req->next);
195 tapdisk_stream_initialize_request(req);
197 return req;
198 }
200 static void
201 tapdisk_stream_print_request(struct tapdisk_stream *s,
202 struct tapdisk_stream_request *sreq)
203 {
204 unsigned long idx = (unsigned long)tapdisk_stream_request_idx(s, sreq);
205 char *buf = (char *)MMAP_VADDR(s->vbd->ring.vstart, idx, 0);
206 write(s->out_fd, buf, sreq->secs << SECTOR_SHIFT);
207 }
209 static void
210 tapdisk_stream_write_data(struct tapdisk_stream *s)
211 {
212 struct tapdisk_stream_request *sreq, *tmp;
214 list_for_each_entry_safe(sreq, tmp, &s->completed_list, next) {
215 if (sreq->seqno != s->completed)
216 break;
218 s->completed++;
219 tapdisk_stream_print_request(s, sreq);
221 list_del_init(&sreq->next);
222 list_add_tail(&sreq->next, &s->free_list);
223 }
224 }
226 static inline void
227 tapdisk_stream_queue_completed(struct tapdisk_stream *s,
228 struct tapdisk_stream_request *sreq)
229 {
230 struct tapdisk_stream_request *itr;
232 list_for_each_entry(itr, &s->completed_list, next)
233 if (sreq->seqno < itr->seqno) {
234 list_add_tail(&sreq->next, &itr->next);
235 return;
236 }
238 list_add_tail(&sreq->next, &s->completed_list);
239 }
241 static void
242 tapdisk_stream_dequeue(void *arg, blkif_response_t *rsp)
243 {
244 struct tapdisk_stream *s = (struct tapdisk_stream *)arg;
245 struct tapdisk_stream_request *sreq = s->requests + rsp->id;
247 list_del_init(&sreq->next);
249 if (rsp->status == BLKIF_RSP_OKAY)
250 tapdisk_stream_queue_completed(s, sreq);
251 else {
252 s->err = EIO;
253 list_add_tail(&sreq->next, &s->free_list);
254 fprintf(stderr, "error reading sector 0x%"PRIu64"\n", sreq->sec);
255 }
257 tapdisk_stream_write_data(s);
258 tapdisk_stream_poll_set(&s->poll);
259 }
261 static void
262 tapdisk_stream_enqueue(event_id_t id, char mode, void *arg)
263 {
264 td_vbd_t *vbd;
265 int i, idx, psize;
266 struct tapdisk_stream *s = (struct tapdisk_stream *)arg;
268 vbd = s->vbd;
269 tapdisk_stream_poll_clear(&s->poll);
271 if (tapdisk_stream_stop(s)) {
272 tapdisk_stream_close_image(s);
273 return;
274 }
276 psize = getpagesize();
278 while (s->cur < s->end && !s->err) {
279 blkif_request_t *breq;
280 td_vbd_request_t *vreq;
281 struct tapdisk_stream_request *sreq;
283 sreq = tapdisk_stream_get_request(s);
284 if (!sreq)
285 break;
287 idx = tapdisk_stream_request_idx(s, sreq);
289 sreq->sec = s->cur;
290 sreq->secs = 0;
291 sreq->seqno = s->started++;
293 breq = &sreq->blkif_req;
294 breq->id = idx;
295 breq->nr_segments = 0;
296 breq->sector_number = sreq->sec;
297 breq->operation = BLKIF_OP_READ;
299 for (i = 0; i < BLKIF_MAX_SEGMENTS_PER_REQUEST; i++) {
300 uint32_t secs = MIN(s->end - s->cur, psize >> SECTOR_SHIFT);
301 struct blkif_request_segment *seg = breq->seg + i;
303 if (!secs)
304 break;
306 sreq->secs += secs;
307 s->cur += secs;
309 seg->first_sect = 0;
310 seg->last_sect = secs - 1;
311 breq->nr_segments++;
312 }
314 vreq = vbd->request_list + idx;
316 assert(list_empty(&vreq->next));
317 assert(vreq->secs_pending == 0);
319 memcpy(&vreq->req, breq, sizeof(*breq));
320 vbd->received++;
321 vreq->vbd = vbd;
323 tapdisk_vbd_move_request(vreq, &vbd->new_requests);
324 list_add_tail(&sreq->next, &s->pending_list);
325 }
327 tapdisk_vbd_issue_requests(vbd);
328 }
330 static int
331 tapdisk_stream_open_image(struct tapdisk_stream *s, const char *path, int type)
332 {
333 int err;
335 s->id = tapdisk_stream_count++;
337 err = tapdisk_server_initialize();
338 if (err)
339 goto out;
341 err = tapdisk_vbd_initialize(s->id);
342 if (err)
343 goto out;
345 s->vbd = tapdisk_server_get_vbd(s->id);
346 if (!s->vbd) {
347 err = ENODEV;
348 goto out;
349 }
351 tapdisk_vbd_set_callback(s->vbd, tapdisk_stream_dequeue, s);
353 err = tapdisk_vbd_open_vdi(s->vbd, path, type,
354 TAPDISK_STORAGE_TYPE_DEFAULT,
355 TD_OPEN_RDONLY);
356 if (err)
357 goto out;
359 s->vbd->reopened = 1;
360 err = 0;
362 out:
363 if (err)
364 fprintf(stderr, "failed to open %s: %d\n", path, err);
365 return err;
366 }
368 static void
369 tapdisk_stream_close_image(struct tapdisk_stream *s)
370 {
371 td_vbd_t *vbd;
373 vbd = tapdisk_server_get_vbd(s->id);
374 if (vbd) {
375 tapdisk_vbd_close_vdi(vbd);
376 tapdisk_server_remove_vbd(vbd);
377 free((void *)vbd->ring.vstart);
378 free(vbd->name);
379 free(vbd);
380 s->vbd = NULL;
381 }
382 }
384 static int
385 tapdisk_stream_set_position(struct tapdisk_stream *s,
386 uint64_t count, uint64_t skip)
387 {
388 int err;
389 image_t image;
391 err = tapdisk_vbd_get_image_info(s->vbd, &image);
392 if (err) {
393 fprintf(stderr, "failed getting image size: %d\n", err);
394 return err;
395 }
397 if (count == (uint64_t)-1)
398 count = image.size - skip;
400 if (count + skip > image.size) {
401 fprintf(stderr, "0x%"PRIx64" past end of image 0x%"PRIx64"\n",
402 (uint64_t) (count + skip), (uint64_t) image.size);
403 return -EINVAL;
404 }
406 s->start = skip;
407 s->cur = s->start;
408 s->end = s->start + count;
410 return 0;
411 }
413 static int
414 tapdisk_stream_initialize_requests(struct tapdisk_stream *s)
415 {
416 size_t size;
417 td_ring_t *ring;
418 int err, i, psize;
420 ring = &s->vbd->ring;
421 psize = getpagesize();
422 size = psize * BLKTAP_MMAP_REGION_SIZE;
424 /* sneaky -- set up ring->vstart so tapdisk_vbd will use our buffers */
425 err = posix_memalign((void **)&ring->vstart, psize, size);
426 if (err) {
427 fprintf(stderr, "failed to allocate buffers: %d\n", err);
428 ring->vstart = 0;
429 return err;
430 }
432 for (i = 0; i < MAX_REQUESTS; i++) {
433 struct tapdisk_stream_request *req = s->requests + i;
434 tapdisk_stream_initialize_request(req);
435 list_add_tail(&req->next, &s->free_list);
436 }
438 return 0;
439 }
441 static int
442 tapdisk_stream_register_enqueue_event(struct tapdisk_stream *s)
443 {
444 int err;
445 struct tapdisk_stream_poll *p = &s->poll;
447 err = tapdisk_stream_poll_open(p);
448 if (err)
449 goto out;
451 err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
452 p->pipe[POLL_READ], 0,
453 tapdisk_stream_enqueue, s);
454 if (err < 0)
455 goto out;
457 s->enqueue_event_id = err;
458 err = 0;
460 out:
461 if (err)
462 fprintf(stderr, "failed to register event: %d\n", err);
463 return err;
464 }
466 static void
467 tapdisk_stream_unregister_enqueue_event(struct tapdisk_stream *s)
468 {
469 if (s->enqueue_event_id) {
470 tapdisk_server_unregister_event(s->enqueue_event_id);
471 s->enqueue_event_id = 0;
472 }
473 tapdisk_stream_poll_close(&s->poll);
474 }
476 static inline void
477 tapdisk_stream_initialize(struct tapdisk_stream *s)
478 {
479 memset(s, 0, sizeof(*s));
480 s->in_fd = s->out_fd = -1;
481 INIT_LIST_HEAD(&s->free_list);
482 INIT_LIST_HEAD(&s->pending_list);
483 INIT_LIST_HEAD(&s->completed_list);
484 }
486 static int
487 tapdisk_stream_open_fds(struct tapdisk_stream *s)
488 {
489 s->out_fd = dup(STDOUT_FILENO);
490 if (s->out_fd == -1) {
491 fprintf(stderr, "failed to open output: %d\n", errno);
492 return errno;
493 }
495 return 0;
496 }
498 static int
499 tapdisk_stream_open(struct tapdisk_stream *s, const char *path,
500 int type, uint64_t count, uint64_t skip)
501 {
502 int err;
504 tapdisk_stream_initialize(s);
506 err = tapdisk_stream_open_fds(s);
507 if (err)
508 return err;
510 err = tapdisk_stream_open_image(s, path, type);
511 if (err)
512 return err;
514 err = tapdisk_stream_set_position(s, count, skip);
515 if (err)
516 return err;
518 err = tapdisk_stream_initialize_requests(s);
519 if (err)
520 return err;
522 err = tapdisk_stream_register_enqueue_event(s);
523 if (err)
524 return err;
526 return 0;
527 }
529 static void
530 tapdisk_stream_release(struct tapdisk_stream *s)
531 {
532 close(s->out_fd);
533 tapdisk_stream_close_image(s);
534 tapdisk_stream_unregister_enqueue_event(s);
535 }
537 static int
538 tapdisk_stream_run(struct tapdisk_stream *s)
539 {
540 tapdisk_stream_enqueue(s->enqueue_event_id, SCHEDULER_POLL_READ_FD, s);
541 tapdisk_server_run();
542 return s->err;
543 }
545 int
546 main(int argc, char *argv[])
547 {
548 int c, err, type;
549 const char *params;
550 const disk_info_t *info;
551 const char *path;
552 uint64_t count, skip;
553 struct tapdisk_stream stream;
555 err = 0;
556 skip = 0;
557 count = (uint64_t)-1;
558 params = NULL;
560 while ((c = getopt(argc, argv, "n:c:s:h")) != -1) {
561 switch (c) {
562 case 'n':
563 params = optarg;
564 break;
565 case 'c':
566 count = strtoull(optarg, NULL, 10);
567 break;
568 case 's':
569 skip = strtoull(optarg, NULL, 10);
570 break;
571 default:
572 err = EINVAL;
573 case 'h':
574 usage(argv[0], err);
575 }
576 }
578 if (!params)
579 usage(argv[0], EINVAL);
581 type = tapdisk_disktype_parse_params(params, &path);
582 if (type < 0) {
583 err = type;
584 fprintf(stderr, "invalid argument %s: %d\n", params, err);
585 return err;
586 }
588 tapdisk_start_logging("tapdisk-stream");
590 err = tapdisk_stream_open(&stream, path, type, count, skip);
591 if (err)
592 goto out;
594 err = tapdisk_stream_run(&stream);
595 if (err)
596 goto out;
598 err = 0;
600 out:
601 tapdisk_stream_release(&stream);
602 tapdisk_stop_logging();
603 return err;
604 }