Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, 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 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #ifdef HAVE_CONFIG_H
32 : : #include "config.h"
33 : : #endif
34 : :
35 : : #include <stdio.h>
36 : : #include <errno.h>
37 : : #include <fcntl.h>
38 : : #include <stdlib.h>
39 : : #include <string.h>
40 : : #include <assert.h>
41 : : #include <unistd.h>
42 : : #include <sys/mman.h>
43 : :
44 : : #include "list.h"
45 : : #include "scheduler.h"
46 : : #include "tapdisk.h"
47 : : #include "tapdisk-server.h"
48 : : #include "tapdisk-disktype.h"
49 : :
50 : : #define POLL_READ 0
51 : : #define POLL_WRITE 1
52 : :
53 : : #define MIN(a, b) ((a) < (b) ? (a) : (b))
54 : : #define BUG(_cond) td_panic()
55 : : #define BUG_ON(_cond) if (unlikely(_cond)) { td_panic(); }
56 : :
57 : : #define TD_STREAM_MAX_REQS 16
58 : : #define TD_STREAM_REQ_SIZE (sysconf(_SC_PAGE_SIZE) * 32)
59 : :
60 : : typedef struct tapdisk_stream_request td_stream_req_t;
61 : : typedef struct tapdisk_stream td_stream_t;
62 : :
63 : : struct tapdisk_stream_request {
64 : : void *buf;
65 : : struct td_iovec iov;
66 : : td_vbd_request_t vreq;
67 : : struct list_head entry;
68 : : };
69 : :
70 : : struct tapdisk_stream {
71 : : td_vbd_t *vbd;
72 : :
73 : : unsigned int id;
74 : : int in_fd;
75 : : int out_fd;
76 : :
77 : : int err;
78 : :
79 : : td_sector_t sec_in;
80 : : td_sector_t sec_out;
81 : : uint64_t count;
82 : :
83 : : struct list_head pending_list;
84 : : struct list_head completed_list;
85 : :
86 : : td_stream_req_t reqs[TD_STREAM_MAX_REQS];
87 : : td_stream_req_t *free[TD_STREAM_MAX_REQS];
88 : : int n_free;
89 : : };
90 : :
91 : : static unsigned int tapdisk_stream_count;
92 : :
93 : : static void tapdisk_stream_close_image(td_stream_t *);
94 : : static void tapdisk_stream_queue_requests(td_stream_t *);
95 : :
96 : : static void
97 : 0 : usage(const char *app, int err)
98 : : {
99 : : printf("usage: %s <-n type:/path/to/image> "
100 : : "[-c sector count] [-s skip sectors]\n", app);
101 : 0 : exit(err);
102 : : }
103 : :
104 : : static inline int
105 : : tapdisk_stream_stop(td_stream_t *s)
106 : : {
107 : 0 : return (list_empty(&s->pending_list) && (!s->count || s->err));
108 : : }
109 : :
110 : : static int
111 : 0 : tapdisk_stream_req_create(td_stream_req_t *req)
112 : : {
113 : : int prot, flags;
114 : :
115 : : memset(req, 0, sizeof(*req));
116 : 0 : INIT_LIST_HEAD(&req->entry);
117 : :
118 : 0 : prot = PROT_READ|PROT_WRITE;
119 : 0 : flags = MAP_ANONYMOUS|MAP_PRIVATE;
120 : :
121 : 0 : req->buf = mmap(NULL, TD_STREAM_REQ_SIZE, prot, flags, -1, 0);
122 : 0 : if (req->buf == MAP_FAILED) {
123 : 0 : req->buf = NULL;
124 : 0 : return -errno;
125 : : }
126 : :
127 : : return 0;
128 : : }
129 : :
130 : : static void
131 : 0 : tapdisk_stream_req_destroy(td_stream_req_t *req)
132 : : {
133 : 0 : if (req->buf) {
134 : 0 : int err = munmap(req->iov.base, TD_STREAM_REQ_SIZE);
135 : 0 : BUG_ON(err);
136 : 0 : req->iov.base = NULL;
137 : : }
138 : 0 : }
139 : :
140 : : td_stream_req_t *
141 : 0 : tapdisk_stream_alloc_req(td_stream_t *s)
142 : : {
143 : 0 : td_stream_req_t *req = NULL;
144 : :
145 : 0 : if (likely(s->n_free))
146 : 0 : req = s->free[--s->n_free];
147 : :
148 : 0 : return req;
149 : : }
150 : :
151 : : void
152 : 0 : tapdisk_stream_free_req(td_stream_t *s, td_stream_req_t *req)
153 : : {
154 : 0 : BUG_ON(s->n_free >= MAX_REQUESTS);
155 : 0 : BUG_ON(!list_empty(&req->entry));
156 : 0 : s->free[s->n_free++] = req;
157 : 0 : }
158 : :
159 : : static void
160 : 0 : tapdisk_stream_destroy_reqs(td_stream_t *s)
161 : : {
162 : 0 : td_stream_req_t *req;
163 : :
164 : : do {
165 : 0 : req = tapdisk_stream_alloc_req(s);
166 : 0 : if (!req)
167 : : break;
168 : :
169 : 0 : tapdisk_stream_req_destroy(req);
170 : 0 : } while (1);
171 : 0 : }
172 : :
173 : : static int
174 : 0 : tapdisk_stream_create_reqs(td_stream_t *s)
175 : : {
176 : : int i, err;
177 : :
178 : 0 : s->n_free = 0;
179 : :
180 : 0 : for (i = 0; i < TD_STREAM_MAX_REQS; i++) {
181 : 0 : td_stream_req_t *req = &s->reqs[i];
182 : :
183 : 0 : err = tapdisk_stream_req_create(req);
184 : 0 : if (err)
185 : : goto fail;
186 : :
187 : 0 : tapdisk_stream_free_req(s, req);
188 : : }
189 : :
190 : : return 0;
191 : :
192 : : fail:
193 : 0 : tapdisk_stream_destroy_reqs(s);
194 : 0 : return err;
195 : : }
196 : :
197 : : static int
198 : 0 : tapdisk_stream_print_request(td_stream_t *s, td_stream_req_t *req)
199 : : {
200 : 0 : struct td_iovec *iov = &req->iov;
201 : :
202 : 0 : int gcc = write(s->out_fd, iov->base, (size_t)iov->secs << SECTOR_SHIFT);
203 : : if (gcc) {};
204 : :
205 : 0 : return iov->secs;
206 : : }
207 : :
208 : : static void
209 : 0 : tapdisk_stream_write_data(td_stream_t *s)
210 : : {
211 : : td_stream_req_t *req, *next;
212 : :
213 : 0 : list_for_each_entry_safe(req, next, &s->completed_list, entry) {
214 : 0 : if (req->vreq.sec != s->sec_out)
215 : : break;
216 : :
217 : 0 : s->sec_out += tapdisk_stream_print_request(s, req);
218 : :
219 : 0 : list_del_init(&req->entry);
220 : 0 : tapdisk_stream_free_req(s, req);
221 : : }
222 : 0 : }
223 : :
224 : : static inline void
225 : : tapdisk_stream_queue_completed(td_stream_t *s, td_stream_req_t *req)
226 : : {
227 : : td_stream_req_t *itr;
228 : :
229 : 0 : list_for_each_entry(itr, &s->completed_list, entry)
230 : 0 : if (req->vreq.sec < itr->vreq.sec)
231 : : break;
232 : :
233 : 0 : list_add_tail(&req->entry, &itr->entry);
234 : : }
235 : :
236 : : static void
237 : 0 : tapdisk_stream_complete_request(td_stream_t *s, td_stream_req_t *req,
238 : : int error, int final)
239 : : {
240 : 0 : list_del_init(&req->entry);
241 : :
242 : 0 : if (likely(!error))
243 : : tapdisk_stream_queue_completed(s, req);
244 : : else {
245 : 0 : s->err = EIO;
246 : 0 : tapdisk_stream_free_req(s, req);
247 : 0 : fprintf(stderr, "error reading sector 0x%"PRIx64"\n",
248 : : req->vreq.sec);
249 : : }
250 : :
251 : 0 : if (!final)
252 : : return;
253 : :
254 : 0 : tapdisk_stream_write_data(s);
255 : :
256 : 0 : if (tapdisk_stream_stop(s)) {
257 : 0 : tapdisk_stream_close_image(s);
258 : 0 : return;
259 : : }
260 : :
261 : 0 : tapdisk_stream_queue_requests(s);
262 : : }
263 : :
264 : : static void
265 : 0 : __tapdisk_stream_request_cb(td_vbd_request_t *vreq, int error,
266 : : void *token, int final)
267 : : {
268 : 0 : td_stream_req_t *req = container_of(vreq, td_stream_req_t, vreq);
269 : 0 : td_stream_t *s = token;
270 : :
271 : 0 : tapdisk_stream_complete_request(s, req, error, final);
272 : 0 : }
273 : :
274 : : static void
275 : 0 : tapdisk_stream_queue_request(td_stream_t *s, td_stream_req_t *req)
276 : : {
277 : : td_vbd_request_t *vreq;
278 : : struct td_iovec *iov;
279 : : int secs, err;
280 : :
281 : 0 : iov = &req->iov;
282 : 0 : secs = MIN(TD_STREAM_REQ_SIZE >> SECTOR_SHIFT, s->count);
283 : :
284 : 0 : iov->base = req->buf;
285 : 0 : iov->secs = secs;
286 : :
287 : 0 : vreq = &req->vreq;
288 : 0 : vreq->iov = iov;
289 : 0 : vreq->iovcnt = 1;
290 : 0 : vreq->sec = s->sec_in;
291 : 0 : vreq->op = TD_OP_READ;
292 : 0 : vreq->name = NULL;
293 : 0 : vreq->token = s;
294 : 0 : vreq->cb = __tapdisk_stream_request_cb;
295 : :
296 : 0 : s->count -= secs;
297 : 0 : s->sec_in += secs;
298 : :
299 : 0 : err = tapdisk_vbd_queue_request(s->vbd, vreq);
300 : 0 : if (err)
301 : 0 : tapdisk_stream_complete_request(s, req, err, 1);
302 : :
303 : 0 : list_add_tail(&req->entry, &s->pending_list);
304 : 0 : }
305 : :
306 : : static void
307 : 0 : tapdisk_stream_queue_requests(td_stream_t *s)
308 : : {
309 : :
310 : 0 : while (s->count && !s->err) {
311 : : td_stream_req_t *req;
312 : :
313 : 0 : req = tapdisk_stream_alloc_req(s);
314 : 0 : if (!req)
315 : : break;
316 : :
317 : 0 : tapdisk_stream_queue_request(s, req);
318 : : }
319 : 0 : }
320 : :
321 : : static int
322 : 0 : tapdisk_stream_open_image(struct tapdisk_stream *s, const char *name)
323 : : {
324 : : int err;
325 : :
326 : 0 : s->id = tapdisk_stream_count++;
327 : :
328 : 0 : err = tapdisk_server_initialize(NULL, NULL);
329 : 0 : if (err)
330 : : goto out;
331 : :
332 : 0 : err = tapdisk_vbd_initialize(-1, -1, s->id);
333 : 0 : if (err)
334 : : goto out;
335 : :
336 : 0 : s->vbd = tapdisk_server_get_vbd(s->id);
337 : 0 : if (!s->vbd) {
338 : : err = ENODEV;
339 : : goto out;
340 : : }
341 : :
342 : 0 : err = tapdisk_vbd_open_vdi(s->vbd, name, TD_OPEN_RDONLY, -1);
343 : 0 : if (err)
344 : : goto out;
345 : :
346 : 0 : err = 0;
347 : :
348 : : out:
349 : 0 : if (err)
350 : 0 : fprintf(stderr, "failed to open %s: %d\n", name, err);
351 : 0 : return err;
352 : : }
353 : :
354 : : static void
355 : 0 : tapdisk_stream_close_image(td_stream_t *s)
356 : : {
357 : : td_vbd_t *vbd;
358 : :
359 : 0 : vbd = tapdisk_server_get_vbd(s->id);
360 : 0 : if (vbd) {
361 : 0 : tapdisk_vbd_close_vdi(vbd);
362 : 0 : tapdisk_server_remove_vbd(vbd);
363 : 0 : tapdisk_vbd_free(vbd);
364 : 0 : s->vbd = NULL;
365 : : }
366 : 0 : }
367 : :
368 : : static int
369 : 0 : tapdisk_stream_set_position(td_stream_t *s,
370 : : uint64_t count, uint64_t skip)
371 : : {
372 : : int err;
373 : : td_disk_info_t info;
374 : :
375 : 0 : err = tapdisk_vbd_get_disk_info(s->vbd, &info);
376 : 0 : if (err) {
377 : 0 : fprintf(stderr, "failed getting image size: %d\n", err);
378 : 0 : return err;
379 : : }
380 : :
381 : 0 : if (count == -1LL)
382 : 0 : count = info.size - skip;
383 : :
384 : 0 : if (count + skip > info.size) {
385 : 0 : fprintf(stderr, "0x%"PRIx64" past end of image 0x%"PRIx64"\n",
386 : : count + skip, info.size);
387 : : return -EINVAL;
388 : : }
389 : :
390 : 0 : s->sec_in = skip;
391 : 0 : s->sec_out = skip;
392 : 0 : s->count = count;
393 : :
394 : 0 : return 0;
395 : : }
396 : :
397 : : void
398 : 0 : __tapdisk_stream_event_cb(event_id_t id, char mode, void *arg)
399 : : {
400 : 0 : }
401 : :
402 : : static int
403 : 0 : tapdisk_stream_open_fds(struct tapdisk_stream *s)
404 : : {
405 : 0 : s->out_fd = dup(STDOUT_FILENO);
406 : 0 : if (s->out_fd == -1) {
407 : 0 : fprintf(stderr, "failed to open output: %d\n", errno);
408 : 0 : return errno;
409 : : }
410 : :
411 : : return 0;
412 : : }
413 : :
414 : : static void
415 : 0 : tapdisk_stream_close(struct tapdisk_stream *s)
416 : : {
417 : 0 : tapdisk_stream_destroy_reqs(s);
418 : :
419 : 0 : tapdisk_stream_close_image(s);
420 : :
421 : 0 : if (s->out_fd >= 0) {
422 : 0 : close(s->out_fd);
423 : 0 : s->out_fd = -1;
424 : : }
425 : 0 : }
426 : :
427 : : static int
428 : 0 : tapdisk_stream_open(struct tapdisk_stream *s, const char *name,
429 : : uint64_t count, uint64_t skip)
430 : : {
431 : 0 : int err = 0;
432 : :
433 : : memset(s, 0, sizeof(*s));
434 : 0 : s->in_fd = s->out_fd = -1;
435 : 0 : INIT_LIST_HEAD(&s->pending_list);
436 : 0 : INIT_LIST_HEAD(&s->completed_list);
437 : :
438 : : if (!err)
439 : 0 : err = tapdisk_stream_open_fds(s);
440 : 0 : if (!err)
441 : 0 : err = tapdisk_stream_open_image(s, name);
442 : 0 : if (!err)
443 : 0 : err = tapdisk_stream_set_position(s, count, skip);
444 : 0 : if (!err)
445 : 0 : err = tapdisk_stream_create_reqs(s);
446 : :
447 : 0 : if (err)
448 : 0 : tapdisk_stream_close(s);
449 : :
450 : 0 : return err;
451 : : }
452 : :
453 : : static int
454 : : tapdisk_stream_run(struct tapdisk_stream *s)
455 : : {
456 : 0 : tapdisk_stream_queue_requests(s);
457 : 0 : tapdisk_server_run();
458 : 0 : return s->err;
459 : : }
460 : :
461 : : int
462 : 0 : main(int argc, char *argv[])
463 : : {
464 : : int c, err;
465 : : const char *params;
466 : : uint64_t count, skip;
467 : : struct tapdisk_stream stream;
468 : :
469 : 0 : err = 0;
470 : 0 : skip = 0;
471 : 0 : count = (uint64_t)-1;
472 : 0 : params = NULL;
473 : :
474 : 0 : while ((c = getopt(argc, argv, "n:c:s:h")) != -1) {
475 : 0 : switch (c) {
476 : : case 'n':
477 : 0 : params = optarg;
478 : 0 : break;
479 : : case 'c':
480 : 0 : count = strtoull(optarg, NULL, 10);
481 : 0 : break;
482 : : case 's':
483 : 0 : skip = strtoull(optarg, NULL, 10);
484 : 0 : break;
485 : : default:
486 : 0 : err = EINVAL;
487 : : case 'h':
488 : 0 : usage(argv[0], err);
489 : : }
490 : : }
491 : :
492 : 0 : if (!params)
493 : 0 : usage(argv[0], EINVAL);
494 : :
495 : 0 : tapdisk_start_logging("tapdisk-stream", "daemon");
496 : :
497 : 0 : err = tapdisk_stream_open(&stream, params, count, skip);
498 : 0 : if (err)
499 : : goto out;
500 : :
501 : 0 : err = tapdisk_stream_run(&stream);
502 : 0 : if (err)
503 : : goto out;
504 : :
505 : 0 : err = 0;
506 : :
507 : : out:
508 : 0 : tapdisk_stream_close(&stream);
509 : 0 : tapdisk_stop_logging();
510 : 0 : return err;
511 : : }
|