Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2020 Intel Corporation.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : : #include "spdk/config.h"
8 : :
9 : : #include "spdk/bdev.h"
10 : : #include "spdk/event.h"
11 : : #include "spdk/fd.h"
12 : : #include "spdk/string.h"
13 : : #include "spdk/util.h"
14 : : #include "spdk/vmd.h"
15 : :
16 : : #include <libaio.h>
17 : :
18 : : #ifdef SPDK_CONFIG_URING
19 : : #include <liburing.h>
20 : : #endif
21 : :
22 : : #define TIMESPEC_TO_MS(time) ((time.tv_sec * 1000) + (time.tv_nsec / 1000000))
23 : : #define STATUS_POLLER_PERIOD_SEC 1
24 : :
25 : : struct spdk_dd_opts {
26 : : char *input_file;
27 : : char *output_file;
28 : : char *input_file_flags;
29 : : char *output_file_flags;
30 : : char *input_bdev;
31 : : char *output_bdev;
32 : : uint64_t input_offset;
33 : : uint64_t output_offset;
34 : : int64_t io_unit_size;
35 : : int64_t io_unit_count;
36 : : uint32_t queue_depth;
37 : : bool aio;
38 : : bool sparse;
39 : : };
40 : :
41 : : static struct spdk_dd_opts g_opts = {
42 : : .io_unit_size = 4096,
43 : : .queue_depth = 2,
44 : : };
45 : :
46 : : enum dd_submit_type {
47 : : DD_POPULATE,
48 : : DD_READ,
49 : : DD_WRITE,
50 : : };
51 : :
52 : : struct dd_io {
53 : : uint64_t offset;
54 : : uint64_t length;
55 : : struct iocb iocb;
56 : : enum dd_submit_type type;
57 : : #ifdef SPDK_CONFIG_URING
58 : : int idx;
59 : : #endif
60 : : void *buf;
61 : : STAILQ_ENTRY(dd_io) link;
62 : : };
63 : :
64 : : enum dd_target_type {
65 : : DD_TARGET_TYPE_FILE,
66 : : DD_TARGET_TYPE_BDEV,
67 : : };
68 : :
69 : : struct dd_target {
70 : : enum dd_target_type type;
71 : :
72 : : union {
73 : : struct {
74 : : struct spdk_bdev *bdev;
75 : : struct spdk_bdev_desc *desc;
76 : : struct spdk_io_channel *ch;
77 : : } bdev;
78 : :
79 : : #ifdef SPDK_CONFIG_URING
80 : : struct {
81 : : int fd;
82 : : int idx;
83 : : } uring;
84 : : #endif
85 : : struct {
86 : : int fd;
87 : : } aio;
88 : : } u;
89 : :
90 : : /* Block size of underlying device. */
91 : : uint32_t block_size;
92 : :
93 : : /* Position of next I/O in bytes */
94 : : uint64_t pos;
95 : :
96 : : /* Total size of target in bytes */
97 : : uint64_t total_size;
98 : :
99 : : bool open;
100 : : };
101 : :
102 : : struct dd_job {
103 : : struct dd_target input;
104 : : struct dd_target output;
105 : :
106 : : struct dd_io *ios;
107 : :
108 : : union {
109 : : #ifdef SPDK_CONFIG_URING
110 : : struct {
111 : : struct io_uring ring;
112 : : bool active;
113 : : struct spdk_poller *poller;
114 : : } uring;
115 : : #endif
116 : : struct {
117 : : io_context_t io_ctx;
118 : : struct spdk_poller *poller;
119 : : } aio;
120 : : } u;
121 : :
122 : : uint32_t outstanding;
123 : : uint64_t copy_size;
124 : : STAILQ_HEAD(, dd_io) seek_queue;
125 : :
126 : : struct timespec start_time;
127 : : uint64_t total_bytes;
128 : : uint64_t incremental_bytes;
129 : : struct spdk_poller *status_poller;
130 : : };
131 : :
132 : : struct dd_flags {
133 : : char *name;
134 : : int flag;
135 : : };
136 : :
137 : : static struct dd_flags g_flags[] = {
138 : : {"append", O_APPEND},
139 : : {"direct", O_DIRECT},
140 : : {"directory", O_DIRECTORY},
141 : : {"dsync", O_DSYNC},
142 : : {"noatime", O_NOATIME},
143 : : {"noctty", O_NOCTTY},
144 : : {"nofollow", O_NOFOLLOW},
145 : : {"nonblock", O_NONBLOCK},
146 : : {"sync", O_SYNC},
147 : : {NULL, 0}
148 : : };
149 : :
150 : : static struct dd_job g_job = {};
151 : : static int g_error = 0;
152 : : static bool g_interrupt;
153 : :
154 : : static void dd_target_seek(struct dd_io *io);
155 : : static void _dd_bdev_seek_hole_done(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg);
156 : :
157 : : static void
158 : 252 : dd_cleanup_bdev(struct dd_target io)
159 : : {
160 : : /* This can be only on the error path.
161 : : * To prevent the SEGV, need add checks here.
162 : : */
163 [ + - # # : 252 : if (io.u.bdev.ch) {
# # # # ]
164 [ # # # # : 252 : spdk_put_io_channel(io.u.bdev.ch);
# # ]
165 : 0 : }
166 : :
167 [ # # # # : 252 : spdk_bdev_close(io.u.bdev.desc);
# # ]
168 : 252 : }
169 : :
170 : : static void
171 : 419 : dd_exit(int rc)
172 : : {
173 [ + + ]: 419 : if (g_job.input.type == DD_TARGET_TYPE_FILE) {
174 : : #ifdef SPDK_CONFIG_URING
175 [ - + + + ]: 122 : if (g_opts.aio == false) {
176 : 90 : close(g_job.input.u.uring.fd);
177 : : } else
178 : : #endif
179 : : {
180 [ # # # # : 219 : close(g_job.input.u.aio.fd);
# # ]
181 : : }
182 [ + - + + : 110 : } else if (g_job.input.type == DD_TARGET_TYPE_BDEV && g_job.input.open) {
+ + # # ]
183 : 108 : dd_cleanup_bdev(g_job.input);
184 : 0 : }
185 : :
186 [ + + # # : 419 : if (g_job.output.type == DD_TARGET_TYPE_FILE) {
# # ]
187 : : #ifdef SPDK_CONFIG_URING
188 [ - + + + ]: 108 : if (g_opts.aio == false) {
189 : 76 : close(g_job.output.u.uring.fd);
190 : : } else
191 : : #endif
192 : : {
193 [ # # # # : 199 : close(g_job.output.u.aio.fd);
# # # # ]
194 : : }
195 [ + - + + : 144 : } else if (g_job.output.type == DD_TARGET_TYPE_BDEV && g_job.output.open) {
+ - # # #
# # # #
# ]
196 : 144 : dd_cleanup_bdev(g_job.output);
197 : 0 : }
198 : :
199 [ + + + + : 419 : if (g_job.input.type == DD_TARGET_TYPE_FILE || g_job.output.type == DD_TARGET_TYPE_FILE) {
# # # # ]
200 : : #ifdef SPDK_CONFIG_URING
201 [ - + + + ]: 150 : if (g_opts.aio == false) {
202 : 118 : spdk_poller_unregister(&g_job.u.uring.poller);
203 : : } else
204 : : #endif
205 : : {
206 [ # # # # ]: 269 : spdk_poller_unregister(&g_job.u.aio.poller);
207 : : }
208 : 0 : }
209 : :
210 : 419 : spdk_poller_unregister(&g_job.status_poller);
211 : :
212 : 419 : spdk_app_stop(rc);
213 : 419 : }
214 : :
215 : : static void
216 : 972 : dd_show_progress(bool finish)
217 : : {
218 : 972 : char *unit_str[5] = {"", "k", "M", "G", "T"};
219 : 972 : char *speed_type_str[2] = {"", "average "};
220 : 972 : char *size_unit_str = "";
221 : 972 : char *speed_unit_str = "";
222 : : char *speed_type;
223 : 972 : uint64_t size_unit = 1;
224 : 972 : uint64_t speed_unit = 1;
225 : : uint64_t speed, tmp_speed;
226 : 972 : int i = 0;
227 : : uint64_t milliseconds;
228 : : uint64_t size, tmp_size;
229 : :
230 [ # # ]: 972 : size = g_job.incremental_bytes;
231 : :
232 [ # # ]: 972 : g_job.incremental_bytes = 0;
233 [ # # ]: 972 : g_job.total_bytes += size;
234 : :
235 [ + + # # ]: 972 : if (finish) {
236 : 202 : struct timespec time_now;
237 : :
238 [ - + ]: 333 : clock_gettime(CLOCK_REALTIME, &time_now);
239 : :
240 [ + + # # : 333 : milliseconds = spdk_max(1, TIMESPEC_TO_MS(time_now) - TIMESPEC_TO_MS(g_job.start_time));
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
241 [ # # ]: 333 : size = g_job.total_bytes;
242 : 0 : } else {
243 : 639 : milliseconds = STATUS_POLLER_PERIOD_SEC * 1000;
244 : : }
245 : :
246 : : /* Find the right unit for size displaying (B vs kB vs MB vs GB vs TB) */
247 : 972 : tmp_size = size;
248 [ + + ]: 2430 : while (tmp_size > 1024 * 10) {
249 [ # # ]: 1458 : tmp_size >>= 10;
250 [ # # ]: 1458 : size_unit <<= 10;
251 [ # # # # : 1458 : size_unit_str = unit_str[++i];
# # # # ]
252 [ - + ]: 1458 : if (i == 4) {
253 : 0 : break;
254 : : }
255 : : }
256 : :
257 [ + + # # : 972 : speed_type = finish ? speed_type_str[1] : speed_type_str[0];
# # # # #
# # # #
# ]
258 [ - + ]: 972 : speed = (size * 1000) / milliseconds;
259 : :
260 : 972 : i = 0;
261 : :
262 : : /* Find the right unit for speed displaying (Bps vs kBps vs MBps vs GBps vs TBps) */
263 : 972 : tmp_speed = speed;
264 [ + + ]: 2678 : while (tmp_speed > 1024 * 10) {
265 [ # # ]: 1706 : tmp_speed >>= 10;
266 [ # # ]: 1706 : speed_unit <<= 10;
267 [ # # # # : 1706 : speed_unit_str = unit_str[++i];
# # # # ]
268 [ - + ]: 1706 : if (i == 4) {
269 : 0 : break;
270 : : }
271 : : }
272 : :
273 [ - + ]: 1883 : printf("\33[2K\rCopying: %" PRIu64 "/%" PRIu64 " [%sB] (%s%" PRIu64 " %sBps)",
274 [ - + - + : 972 : g_job.total_bytes / size_unit, g_job.copy_size / size_unit, size_unit_str, speed_type,
# # # # ]
275 [ - + ]: 911 : speed / speed_unit, speed_unit_str);
276 : 972 : fflush(stdout);
277 : 972 : }
278 : :
279 : : static int
280 : 639 : dd_status_poller(void *ctx)
281 : : {
282 : 639 : dd_show_progress(false);
283 : 639 : return SPDK_POLLER_BUSY;
284 : : }
285 : :
286 : : static void
287 : 0 : dd_finalize_output(void)
288 : : {
289 : : off_t curr_offset;
290 : 0 : int rc = 0;
291 : :
292 [ # # # # ]: 0 : if (g_job.outstanding > 0) {
293 : 0 : return;
294 : : }
295 : :
296 [ # # # # ]: 0 : if (g_opts.output_file) {
297 [ # # # # : 0 : curr_offset = lseek(g_job.output.u.aio.fd, 0, SEEK_END);
# # # # ]
298 [ # # ]: 0 : if (curr_offset == (off_t) -1) {
299 [ # # ]: 0 : SPDK_ERRLOG("Could not seek output file for finalize: %s\n", strerror(errno));
300 [ # # ]: 0 : g_error = errno;
301 [ # # # # : 0 : } else if ((uint64_t)curr_offset < g_job.copy_size + g_job.output.pos) {
# # # # ]
302 [ # # # # : 0 : rc = ftruncate(g_job.output.u.aio.fd, g_job.copy_size + g_job.output.pos);
# # # # #
# # # #
# ]
303 [ # # ]: 0 : if (rc != 0) {
304 [ # # ]: 0 : SPDK_ERRLOG("Could not truncate output file for finalize: %s\n", strerror(errno));
305 [ # # ]: 0 : g_error = errno;
306 : 0 : }
307 : 0 : }
308 : 0 : }
309 : :
310 [ # # ]: 0 : if (g_error == 0) {
311 : 0 : dd_show_progress(true);
312 : 0 : printf("\n\n");
313 : 0 : }
314 : 0 : dd_exit(g_error);
315 : 0 : }
316 : :
317 : : #ifdef SPDK_CONFIG_URING
318 : : static void
319 : 557632 : dd_uring_submit(struct dd_io *io, struct dd_target *target, uint64_t length, uint64_t offset)
320 : : {
321 : : struct io_uring_sqe *sqe;
322 : :
323 : 557632 : sqe = io_uring_get_sqe(&g_job.u.uring.ring);
324 [ + + - + ]: 557632 : if (io->type == DD_READ || io->type == DD_POPULATE) {
325 : 295216 : io_uring_prep_read_fixed(sqe, target->u.uring.idx, io->buf, length, offset, io->idx);
326 : : } else {
327 : 262416 : io_uring_prep_write_fixed(sqe, target->u.uring.idx, io->buf, length, offset, io->idx);
328 : : }
329 : 557632 : sqe->flags |= IOSQE_FIXED_FILE;
330 : 557632 : io_uring_sqe_set_data(sqe, io);
331 : 557632 : io_uring_submit(&g_job.u.uring.ring);
332 : 557632 : }
333 : : #endif
334 : :
335 : : static void
336 : 2772996 : _dd_write_bdev_done(struct spdk_bdev_io *bdev_io,
337 : : bool success,
338 : : void *cb_arg)
339 : : {
340 : 2772996 : struct dd_io *io = cb_arg;
341 : :
342 [ - + # # : 2772996 : assert(g_job.outstanding > 0);
# # ]
343 : 2772996 : g_job.outstanding--;
344 : 2772996 : spdk_bdev_free_io(bdev_io);
345 : 2772996 : dd_target_seek(io);
346 : 2772996 : }
347 : :
348 : : static void
349 : 5008886 : dd_target_write(struct dd_io *io)
350 : : {
351 : 5008886 : struct dd_target *target = &g_job.output;
352 [ - + # # : 5008886 : uint64_t length = SPDK_CEIL_DIV(io->length, target->block_size) * target->block_size;
# # # # #
# # # # #
# # # # ]
353 [ # # # # ]: 5008886 : uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size;
354 [ # # # # ]: 5008886 : uint64_t read_offset = io->offset - read_region_start;
355 [ # # # # ]: 5008886 : uint64_t write_region_start = g_opts.output_offset * g_opts.io_unit_size;
356 : 5008886 : uint64_t write_offset = write_region_start + read_offset;
357 : 5008886 : int rc = 0;
358 : :
359 [ + - - + : 5008886 : if (g_error != 0 || g_interrupt == true) {
- + ]
360 [ # # # # ]: 0 : if (g_job.outstanding == 0) {
361 [ # # ]: 0 : if (g_error == 0) {
362 : 0 : dd_show_progress(true);
363 : 0 : printf("\n\n");
364 : 0 : }
365 : 0 : dd_exit(g_error);
366 : 0 : }
367 : 0 : return;
368 : : }
369 : :
370 [ # # # # : 5008886 : g_job.incremental_bytes += io->length;
# # ]
371 : 5008886 : g_job.outstanding++;
372 [ # # # # ]: 5008886 : io->type = DD_WRITE;
373 : :
374 [ + + # # : 5008886 : if (target->type == DD_TARGET_TYPE_FILE) {
# # ]
375 : : #ifdef SPDK_CONFIG_URING
376 [ - + + + ]: 262440 : if (g_opts.aio == false) {
377 : 262416 : dd_uring_submit(io, target, length, write_offset);
378 : : } else
379 : : #endif
380 : : {
381 [ # # ]: 1973474 : struct iocb *iocb = &io->iocb;
382 : :
383 [ # # # # : 1973474 : io_prep_pwrite(iocb, target->u.aio.fd, io->buf, length, write_offset);
# # # # #
# # # ]
384 [ # # # # ]: 1973474 : iocb->data = io;
385 [ - + # # : 1973474 : if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) {
# # # # ]
386 [ # # # # ]: 0 : rc = -errno;
387 : 0 : }
388 : : }
389 [ + - # # : 2772996 : } else if (target->type == DD_TARGET_TYPE_BDEV) {
# # ]
390 [ # # # # : 2772996 : rc = spdk_bdev_write(target->u.bdev.desc, target->u.bdev.ch, io->buf, write_offset, length,
# # # # #
# # # # #
# # # # #
# ]
391 : 0 : _dd_write_bdev_done, io);
392 : 0 : }
393 : :
394 [ - + ]: 5008886 : if (rc != 0) {
395 [ # # ]: 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
396 [ # # # # : 0 : assert(g_job.outstanding > 0);
# # ]
397 : 0 : g_job.outstanding--;
398 : 0 : g_error = rc;
399 [ # # # # ]: 0 : if (g_job.outstanding == 0) {
400 : 0 : dd_exit(rc);
401 : 0 : }
402 : 0 : return;
403 : : }
404 : 0 : }
405 : :
406 : : static void
407 : 3021747 : _dd_read_bdev_done(struct spdk_bdev_io *bdev_io,
408 : : bool success,
409 : : void *cb_arg)
410 : : {
411 : 3021747 : struct dd_io *io = cb_arg;
412 : :
413 : 3021747 : spdk_bdev_free_io(bdev_io);
414 : :
415 [ - + # # : 3021747 : assert(g_job.outstanding > 0);
# # ]
416 : 3021747 : g_job.outstanding--;
417 : 3021747 : dd_target_write(io);
418 : 3021747 : }
419 : :
420 : : static void
421 : 5008886 : dd_target_read(struct dd_io *io)
422 : : {
423 : 5008886 : struct dd_target *target = &g_job.input;
424 : 5008886 : int rc = 0;
425 : :
426 [ + - - + : 5008886 : if (g_error != 0 || g_interrupt == true) {
- + ]
427 [ # # # # ]: 0 : if (g_job.outstanding == 0) {
428 : 0 : dd_exit(g_error);
429 : 0 : }
430 : 0 : return;
431 : : }
432 : :
433 : 5008886 : g_job.outstanding++;
434 [ # # # # ]: 5008886 : io->type = DD_READ;
435 : :
436 [ + + # # : 5008886 : if (target->type == DD_TARGET_TYPE_FILE) {
# # ]
437 : : #ifdef SPDK_CONFIG_URING
438 [ - + + + ]: 295240 : if (g_opts.aio == false) {
439 : 295216 : dd_uring_submit(io, target, io->length, io->offset);
440 : : } else
441 : : #endif
442 : : {
443 [ # # ]: 1691923 : struct iocb *iocb = &io->iocb;
444 : :
445 [ # # # # : 1691923 : io_prep_pread(iocb, target->u.aio.fd, io->buf, io->length, io->offset);
# # # # #
# # # # #
# # # # #
# ]
446 [ # # # # ]: 1691923 : iocb->data = io;
447 [ - + # # : 1691923 : if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) {
# # # # ]
448 [ # # # # ]: 0 : rc = -errno;
449 : 0 : }
450 : : }
451 [ + - # # : 3021747 : } else if (target->type == DD_TARGET_TYPE_BDEV) {
# # ]
452 [ # # # # : 3021747 : rc = spdk_bdev_read(target->u.bdev.desc, target->u.bdev.ch, io->buf, io->offset, io->length,
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
453 : 0 : _dd_read_bdev_done, io);
454 : 0 : }
455 : :
456 [ - + ]: 5008886 : if (rc != 0) {
457 [ # # ]: 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
458 [ # # # # : 0 : assert(g_job.outstanding > 0);
# # ]
459 : 0 : g_job.outstanding--;
460 : 0 : g_error = rc;
461 [ # # # # ]: 0 : if (g_job.outstanding == 0) {
462 : 0 : dd_exit(rc);
463 : 0 : }
464 : 0 : return;
465 : : }
466 : 0 : }
467 : :
468 : : static void
469 : 5 : _dd_target_populate_buffer_done(struct spdk_bdev_io *bdev_io,
470 : : bool success,
471 : : void *cb_arg)
472 : : {
473 : 5 : struct dd_io *io = cb_arg;
474 : :
475 [ - + # # : 5 : assert(g_job.outstanding > 0);
# # ]
476 : 5 : g_job.outstanding--;
477 : 5 : spdk_bdev_free_io(bdev_io);
478 : 5 : dd_target_read(io);
479 : 5 : }
480 : :
481 : : static void
482 : 5009517 : dd_target_populate_buffer(struct dd_io *io)
483 : : {
484 : 5009517 : struct dd_target *target = &g_job.output;
485 [ # # # # ]: 5009517 : uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size;
486 [ # # ]: 5009517 : uint64_t read_offset = g_job.input.pos - read_region_start;
487 [ # # # # ]: 5009517 : uint64_t write_region_start = g_opts.output_offset * g_opts.io_unit_size;
488 : 5009517 : uint64_t write_offset = write_region_start + read_offset;
489 : : uint64_t length;
490 : 5009517 : int rc = 0;
491 : :
492 [ # # # # : 5009517 : io->offset = g_job.input.pos;
# # ]
493 [ # # # # : 5009517 : io->length = spdk_min(io->length, g_job.copy_size - read_offset);
# # # # #
# # # # #
# # # # ]
494 : :
495 [ + + + - : 5009517 : if (io->length == 0 || g_error != 0 || g_interrupt == true) {
- + - + #
# # # ]
496 [ + + # # ]: 631 : if (g_job.outstanding == 0) {
497 [ + - ]: 318 : if (g_error == 0) {
498 : 318 : dd_show_progress(true);
499 : 318 : printf("\n\n");
500 : 0 : }
501 : 318 : dd_exit(g_error);
502 : 0 : }
503 : 631 : return;
504 : : }
505 : :
506 [ # # # # : 5008886 : g_job.input.pos += io->length;
# # ]
507 : :
508 [ + + + + : 5008886 : if ((io->length % target->block_size) == 0) {
# # # # #
# # # ]
509 : 5008881 : dd_target_read(io);
510 : 5008881 : return;
511 : : }
512 : :
513 : : /* Read whole blocks from output to combine buffers later */
514 : 5 : g_job.outstanding++;
515 [ # # # # ]: 5 : io->type = DD_POPULATE;
516 : :
517 [ - + # # : 5 : length = SPDK_CEIL_DIV(io->length, target->block_size) * target->block_size;
# # # # #
# # # # #
# # # # ]
518 : :
519 [ - + # # : 5 : if (target->type == DD_TARGET_TYPE_FILE) {
# # ]
520 : : #ifdef SPDK_CONFIG_URING
521 [ # # # # ]: 0 : if (g_opts.aio == false) {
522 : 0 : dd_uring_submit(io, target, length, write_offset);
523 : : } else
524 : : #endif
525 : : {
526 [ # # ]: 0 : struct iocb *iocb = &io->iocb;
527 : :
528 [ # # # # : 0 : io_prep_pread(iocb, target->u.aio.fd, io->buf, length, write_offset);
# # # # #
# # # ]
529 [ # # # # ]: 0 : iocb->data = io;
530 [ # # # # : 0 : if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) {
# # # # ]
531 [ # # # # ]: 0 : rc = -errno;
532 : 0 : }
533 : : }
534 [ + - # # : 5 : } else if (target->type == DD_TARGET_TYPE_BDEV) {
# # ]
535 [ # # # # : 5 : rc = spdk_bdev_read(target->u.bdev.desc, target->u.bdev.ch, io->buf, write_offset, length,
# # # # #
# # # # #
# # # # #
# ]
536 : 0 : _dd_target_populate_buffer_done, io);
537 : 0 : }
538 : :
539 [ - + ]: 5 : if (rc != 0) {
540 [ # # ]: 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
541 [ # # # # : 0 : assert(g_job.outstanding > 0);
# # ]
542 : 0 : g_job.outstanding--;
543 : 0 : g_error = rc;
544 [ # # # # ]: 0 : if (g_job.outstanding == 0) {
545 : 0 : dd_exit(rc);
546 : 0 : }
547 : 0 : return;
548 : : }
549 : 0 : }
550 : :
551 : : static off_t
552 : 30 : dd_file_seek_data(void)
553 : : {
554 : 30 : off_t next_data_offset = (off_t) -1;
555 : :
556 [ # # # # : 30 : next_data_offset = lseek(g_job.input.u.aio.fd, g_job.input.pos, SEEK_DATA);
# # # # ]
557 : :
558 [ - + ]: 30 : if (next_data_offset == (off_t) -1) {
559 : : /* NXIO with SEEK_DATA means there are no more data to read.
560 : : * But in case of input and output files, we may have to finalize output file
561 : : * inserting a hole to the end of the file.
562 : : */
563 [ # # # # ]: 0 : if (errno == ENXIO) {
564 : 0 : dd_finalize_output();
565 [ # # # # ]: 0 : } else if (g_job.outstanding == 0) {
566 [ # # ]: 0 : SPDK_ERRLOG("Could not seek input file for data: %s\n", strerror(errno));
567 [ # # ]: 0 : g_error = errno;
568 : 0 : dd_exit(g_error);
569 : 0 : }
570 : 0 : }
571 : :
572 : 30 : return next_data_offset;
573 : : }
574 : :
575 : : static off_t
576 : 30 : dd_file_seek_hole(void)
577 : : {
578 : 30 : off_t next_hole_offset = (off_t) -1;
579 : :
580 [ # # # # : 30 : next_hole_offset = lseek(g_job.input.u.aio.fd, g_job.input.pos, SEEK_HOLE);
# # # # ]
581 : :
582 [ - + - - : 30 : if (next_hole_offset == (off_t) -1 && g_job.outstanding == 0) {
# # ]
583 [ # # ]: 0 : SPDK_ERRLOG("Could not seek input file for hole: %s\n", strerror(errno));
584 [ # # ]: 0 : g_error = errno;
585 : 0 : dd_exit(g_error);
586 : 0 : }
587 : :
588 : 30 : return next_hole_offset;
589 : : }
590 : :
591 : : static void
592 : 15 : _dd_bdev_seek_data_done(struct spdk_bdev_io *bdev_io,
593 : : bool success,
594 : : void *cb_arg)
595 : : {
596 : 15 : struct dd_io *io = cb_arg;
597 : 15 : uint64_t next_data_offset_blocks = UINT64_MAX;
598 : 15 : struct dd_target *target = &g_job.input;
599 : 15 : int rc = 0;
600 : :
601 [ + - - + : 15 : if (g_error != 0 || g_interrupt == true) {
- + ]
602 [ # # # # : 0 : STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
# # # # #
# # # # #
# # # # #
# # # ]
603 [ # # # # ]: 0 : if (g_job.outstanding == 0) {
604 [ # # ]: 0 : if (g_error == 0) {
605 : 0 : dd_show_progress(true);
606 [ # # ]: 0 : printf("\n\n");
607 : 0 : }
608 : 0 : dd_exit(g_error);
609 : 0 : }
610 : 0 : return;
611 : : }
612 : :
613 [ - + # # : 15 : assert(g_job.outstanding > 0);
# # ]
614 : 15 : g_job.outstanding--;
615 : :
616 : 15 : next_data_offset_blocks = spdk_bdev_io_get_seek_offset(bdev_io);
617 : 15 : spdk_bdev_free_io(bdev_io);
618 : :
619 : : /* UINT64_MAX means there are no more data to read.
620 : : * But in case of input and output files, we may have to finalize output file
621 : : * inserting a hole to the end of the file.
622 : : */
623 [ - + ]: 15 : if (next_data_offset_blocks == UINT64_MAX) {
624 [ # # # # : 0 : STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
# # # # #
# # # # #
# # # # #
# # # ]
625 : 0 : dd_finalize_output();
626 : 0 : return;
627 : : }
628 : :
629 [ # # # # ]: 15 : g_job.input.pos = next_data_offset_blocks * g_job.input.block_size;
630 : :
631 : 15 : g_job.outstanding++;
632 [ # # # # : 15 : rc = spdk_bdev_seek_hole(target->u.bdev.desc, target->u.bdev.ch,
# # # # #
# # # # #
# # ]
633 [ - + # # : 15 : g_job.input.pos / g_job.input.block_size,
# # ]
634 : 0 : _dd_bdev_seek_hole_done, io);
635 : :
636 [ - + ]: 15 : if (rc != 0) {
637 [ # # ]: 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
638 [ # # # # : 0 : STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
# # # # #
# # # # #
# # # # #
# # # ]
639 [ # # # # : 0 : assert(g_job.outstanding > 0);
# # ]
640 : 0 : g_job.outstanding--;
641 : 0 : g_error = rc;
642 [ # # # # ]: 0 : if (g_job.outstanding == 0) {
643 : 0 : dd_exit(rc);
644 : 0 : }
645 : 0 : }
646 : 0 : }
647 : :
648 : : static void
649 : 15 : _dd_bdev_seek_hole_done(struct spdk_bdev_io *bdev_io,
650 : : bool success,
651 : : void *cb_arg)
652 : : {
653 : 15 : struct dd_io *io = cb_arg;
654 : 15 : struct dd_target *target = &g_job.input;
655 : 15 : uint64_t next_hole_offset_blocks = UINT64_MAX;
656 : : struct dd_io *seek_io;
657 : 15 : int rc = 0;
658 : :
659 : : /* First seek operation is the one in progress, i.e. this one just ended */
660 [ + + # # : 15 : STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
# # # # #
# # # # #
# # # # #
# # # ]
661 : :
662 [ + - - + : 15 : if (g_error != 0 || g_interrupt == true) {
- + ]
663 [ # # # # ]: 0 : if (g_job.outstanding == 0) {
664 [ # # ]: 0 : if (g_error == 0) {
665 : 0 : dd_show_progress(true);
666 [ # # ]: 0 : printf("\n\n");
667 : 0 : }
668 : 0 : dd_exit(g_error);
669 : 0 : }
670 : 0 : return;
671 : : }
672 : :
673 [ - + # # : 15 : assert(g_job.outstanding > 0);
# # ]
674 : 15 : g_job.outstanding--;
675 : :
676 : 15 : next_hole_offset_blocks = spdk_bdev_io_get_seek_offset(bdev_io);
677 : 15 : spdk_bdev_free_io(bdev_io);
678 : :
679 : : /* UINT64_MAX means there are no more holes. */
680 [ + + ]: 15 : if (next_hole_offset_blocks == UINT64_MAX) {
681 [ # # # # : 5 : io->length = g_opts.io_unit_size;
# # ]
682 : 0 : } else {
683 [ # # # # : 10 : io->length = spdk_min((uint64_t)g_opts.io_unit_size,
# # # # #
# # # # #
# # # # ]
684 : : next_hole_offset_blocks * g_job.input.block_size - g_job.input.pos);
685 : : }
686 : :
687 : 15 : dd_target_populate_buffer(io);
688 : :
689 : : /* If input reading is not at the end, start following seek operation in the queue */
690 [ + + + + : 15 : if (!STAILQ_EMPTY(&g_job.seek_queue) && g_job.input.pos < g_job.input.total_size) {
# # # # #
# # # ]
691 [ # # # # ]: 5 : seek_io = STAILQ_FIRST(&g_job.seek_queue);
692 [ - + # # ]: 5 : assert(seek_io != NULL);
693 : 5 : g_job.outstanding++;
694 [ # # # # : 5 : rc = spdk_bdev_seek_data(target->u.bdev.desc, target->u.bdev.ch,
# # # # #
# # # # #
# # ]
695 [ - + # # : 5 : g_job.input.pos / g_job.input.block_size,
# # ]
696 : 0 : _dd_bdev_seek_data_done, seek_io);
697 : :
698 [ - + ]: 5 : if (rc != 0) {
699 [ # # ]: 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
700 [ # # # # : 0 : assert(g_job.outstanding > 0);
# # ]
701 : 0 : g_job.outstanding--;
702 : 0 : g_error = rc;
703 [ # # # # ]: 0 : if (g_job.outstanding == 0) {
704 : 0 : dd_exit(rc);
705 : 0 : }
706 : 0 : }
707 : 0 : }
708 : 0 : }
709 : :
710 : : static void
711 : 5009547 : dd_target_seek(struct dd_io *io)
712 : : {
713 : 5009547 : struct dd_target *target = &g_job.input;
714 [ # # # # ]: 5009547 : uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size;
715 [ # # ]: 5009547 : uint64_t read_offset = g_job.input.pos - read_region_start;
716 : 5009547 : off_t next_data_offset = (off_t) -1;
717 : 5009547 : off_t next_hole_offset = (off_t) -1;
718 : 5009547 : int rc = 0;
719 : :
720 [ + + + + ]: 5009547 : if (!g_opts.sparse) {
721 : 5009472 : dd_target_populate_buffer(io);
722 : 5009472 : return;
723 : : }
724 : :
725 [ + + + - : 75 : if (g_job.copy_size - read_offset == 0 || g_error != 0 || g_interrupt == true) {
- + - + #
# ]
726 [ + + # # ]: 27 : if (g_job.outstanding == 0) {
727 [ + - ]: 15 : if (g_error == 0) {
728 : 15 : dd_show_progress(true);
729 [ - + ]: 15 : printf("\n\n");
730 : 0 : }
731 : 15 : dd_exit(g_error);
732 : 0 : }
733 : 27 : return;
734 : : }
735 : :
736 [ + + # # : 48 : if (target->type == DD_TARGET_TYPE_FILE) {
# # ]
737 : 30 : next_data_offset = dd_file_seek_data();
738 [ - + ]: 30 : if (next_data_offset < 0) {
739 : 0 : return;
740 [ + + # # ]: 30 : } else if ((uint64_t)next_data_offset > g_job.input.pos) {
741 [ # # ]: 20 : g_job.input.pos = next_data_offset;
742 : 0 : }
743 : :
744 : 30 : next_hole_offset = dd_file_seek_hole();
745 [ - + ]: 30 : if (next_hole_offset < 0) {
746 : 0 : return;
747 [ + - # # ]: 30 : } else if ((uint64_t)next_hole_offset > g_job.input.pos) {
748 [ # # # # : 30 : io->length = spdk_min((uint64_t)g_opts.io_unit_size,
# # # # #
# # # #
# ]
749 : : (uint64_t)(next_hole_offset - g_job.input.pos));
750 : 0 : } else {
751 [ # # # # : 0 : io->length = g_opts.io_unit_size;
# # ]
752 : : }
753 : :
754 : 30 : dd_target_populate_buffer(io);
755 [ + - # # : 18 : } else if (target->type == DD_TARGET_TYPE_BDEV) {
# # ]
756 : : /* Check if other seek operation is in progress */
757 [ + + # # : 18 : if (STAILQ_EMPTY(&g_job.seek_queue)) {
# # ]
758 : 10 : g_job.outstanding++;
759 [ # # # # : 10 : rc = spdk_bdev_seek_data(target->u.bdev.desc, target->u.bdev.ch,
# # # # #
# # # # #
# # ]
760 [ - + # # : 10 : g_job.input.pos / g_job.input.block_size,
# # ]
761 : 0 : _dd_bdev_seek_data_done, io);
762 : :
763 : 0 : }
764 : :
765 [ # # # # : 18 : STAILQ_INSERT_TAIL(&g_job.seek_queue, io, link);
# # # # #
# # # # #
# # # # #
# ]
766 : 0 : }
767 : :
768 [ - + ]: 48 : if (rc != 0) {
769 [ # # ]: 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
770 [ # # # # : 0 : assert(g_job.outstanding > 0);
# # ]
771 : 0 : g_job.outstanding--;
772 : 0 : g_error = rc;
773 [ # # # # ]: 0 : if (g_job.outstanding == 0) {
774 : 0 : dd_exit(rc);
775 : 0 : }
776 : 0 : return;
777 : : }
778 : 0 : }
779 : :
780 : : static void
781 : 4223029 : dd_complete_poll(struct dd_io *io)
782 : : {
783 [ - + # # : 4223029 : assert(g_job.outstanding > 0);
# # ]
784 : 4223029 : g_job.outstanding--;
785 : :
786 [ - + + - : 4223029 : switch (io->type) {
# # # # ]
787 : 0 : case DD_POPULATE:
788 : 0 : dd_target_read(io);
789 : 0 : break;
790 : 1987139 : case DD_READ:
791 : 1987139 : dd_target_write(io);
792 : 1987139 : break;
793 : 2235890 : case DD_WRITE:
794 : 2235890 : dd_target_seek(io);
795 : 2235890 : break;
796 : 0 : default:
797 [ # # ]: 0 : assert(false);
798 : : break;
799 : : }
800 : 4223029 : }
801 : :
802 : : #ifdef SPDK_CONFIG_URING
803 : : static int
804 : 3284445 : dd_uring_poll(void *ctx)
805 : : {
806 : : struct io_uring_cqe *cqe;
807 : : struct dd_io *io;
808 : 3284445 : int rc = 0;
809 : : int i;
810 : :
811 [ + + ]: 3842077 : for (i = 0; i < (int)g_opts.queue_depth; i++) {
812 : 3699784 : rc = io_uring_peek_cqe(&g_job.u.uring.ring, &cqe);
813 [ + + ]: 3699784 : if (rc == -EAGAIN) {
814 : 3142152 : break;
815 : : }
816 [ - + ]: 557632 : assert(cqe != NULL);
817 : :
818 : 557632 : io = io_uring_cqe_get_data(cqe);
819 [ - + ]: 557632 : if (cqe->res < 0) {
820 : 0 : SPDK_ERRLOG("%s\n", strerror(-cqe->res));
821 : 0 : dd_exit(cqe->res);
822 : : }
823 : :
824 : 557632 : io_uring_cqe_seen(&g_job.u.uring.ring, cqe);
825 : 557632 : dd_complete_poll(io);
826 : : }
827 : :
828 : 3284445 : return (i ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE);
829 : : }
830 : :
831 : : #endif
832 : :
833 : : static int
834 : 95081587 : dd_aio_poll(void *ctx)
835 : : {
836 : 95024641 : struct io_event events[32];
837 : 95081587 : int rc = 0;
838 : : int i;
839 : 95024641 : struct timespec timeout;
840 : : struct dd_io *io;
841 : :
842 : 95081587 : timeout.tv_sec = 0;
843 [ # # ]: 95081587 : timeout.tv_nsec = 0;
844 : :
845 [ # # # # : 95081587 : rc = io_getevents(g_job.u.aio.io_ctx, 0, 32, events, &timeout);
# # ]
846 : :
847 [ - + ]: 95081587 : if (rc < 0) {
848 [ # # ]: 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
849 : 0 : dd_exit(rc);
850 : 0 : }
851 : :
852 [ + + # # ]: 98746984 : for (i = 0; i < rc; i++) {
853 [ # # # # : 3665397 : io = events[i].data;
# # # # ]
854 [ - + # # : 3665397 : if (events[i].res != io->length) {
# # # # #
# # # #
# ]
855 : 0 : g_error = -ENOSPC;
856 : 0 : }
857 : :
858 : 3665397 : dd_complete_poll(io);
859 : 0 : }
860 : :
861 : 95081587 : return (i ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE);
862 : : }
863 : :
864 : : static int
865 : 538 : dd_open_file(struct dd_target *target, const char *fname, int flags, uint64_t skip_blocks,
866 : : bool input)
867 : : {
868 : : int *fd;
869 : :
870 : : #ifdef SPDK_CONFIG_URING
871 [ - + + + ]: 208 : if (g_opts.aio == false) {
872 : 148 : fd = &target->u.uring.fd;
873 : : } else
874 : : #endif
875 : : {
876 [ # # # # : 390 : fd = &target->u.aio.fd;
# # ]
877 : : }
878 : :
879 : 538 : flags |= O_RDWR;
880 : :
881 [ + + + + : 538 : if (input == false && ((flags & O_DIRECTORY) == 0)) {
# # ]
882 : 226 : flags |= O_CREAT;
883 : 0 : }
884 : :
885 [ + + + + : 538 : if (input == false && ((flags & O_APPEND) == 0)) {
# # ]
886 : 219 : flags |= O_TRUNC;
887 : 0 : }
888 : :
889 [ # # # # ]: 538 : target->type = DD_TARGET_TYPE_FILE;
890 [ - + # # ]: 538 : *fd = open(fname, flags, 0600);
891 [ + + # # ]: 538 : if (*fd < 0) {
892 [ # # ]: 40 : SPDK_ERRLOG("Could not open file %s: %s\n", fname, strerror(errno));
893 [ # # ]: 40 : return *fd;
894 : : }
895 : :
896 [ + + # # : 498 : target->block_size = spdk_max(spdk_fd_get_blocklen(*fd), 1);
# # # # #
# ]
897 : :
898 [ # # # # : 498 : target->total_size = spdk_fd_get_size(*fd);
# # ]
899 [ + + # # : 498 : if (target->total_size == 0) {
# # ]
900 [ # # # # : 280 : target->total_size = g_opts.io_unit_size * g_opts.io_unit_count;
# # # # #
# ]
901 : 0 : }
902 : :
903 [ + + # # ]: 498 : if (input == true) {
904 [ - + + + : 282 : g_opts.queue_depth = spdk_min(g_opts.queue_depth,
- + # # #
# # # # #
# # # # #
# # # #
# ]
905 : : (target->total_size / g_opts.io_unit_size) - skip_blocks + 1);
906 : 0 : }
907 : :
908 [ + + # # ]: 498 : if (g_opts.io_unit_count != 0) {
909 [ # # # # : 142 : g_opts.queue_depth = spdk_min(g_opts.queue_depth, g_opts.io_unit_count);
# # # # #
# # # ]
910 : 0 : }
911 : :
912 : 498 : return 0;
913 : 0 : }
914 : :
915 : : static void
916 : 0 : dd_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
917 : : void *event_ctx)
918 : : {
919 : 0 : SPDK_NOTICELOG("Unsupported bdev event: type %d\n", type);
920 : 0 : }
921 : :
922 : : static int
923 : 254 : dd_open_bdev(struct dd_target *target, const char *bdev_name, uint64_t skip_blocks)
924 : : {
925 : : int rc;
926 : :
927 [ # # # # ]: 254 : target->type = DD_TARGET_TYPE_BDEV;
928 : :
929 [ # # # # : 254 : rc = spdk_bdev_open_ext(bdev_name, true, dd_bdev_event_cb, NULL, &target->u.bdev.desc);
# # ]
930 [ + + ]: 254 : if (rc < 0) {
931 [ # # ]: 2 : SPDK_ERRLOG("Could not open bdev %s: %s\n", bdev_name, strerror(-rc));
932 : 2 : return rc;
933 : : }
934 : :
935 [ # # # # : 252 : target->u.bdev.bdev = spdk_bdev_desc_get_bdev(target->u.bdev.desc);
# # # # #
# # # # #
# # ]
936 [ # # # # ]: 252 : target->open = true;
937 : :
938 [ # # # # : 252 : target->u.bdev.ch = spdk_bdev_get_io_channel(target->u.bdev.desc);
# # # # #
# # # # #
# # ]
939 [ - + # # : 252 : if (target->u.bdev.ch == NULL) {
# # # # #
# ]
940 [ # # # # : 0 : spdk_bdev_close(target->u.bdev.desc);
# # # # ]
941 : 0 : SPDK_ERRLOG("Could not get I/O channel: %s\n", strerror(ENOMEM));
942 : 0 : return -ENOMEM;
943 : : }
944 : :
945 [ # # # # : 252 : target->block_size = spdk_bdev_get_block_size(target->u.bdev.bdev);
# # # # #
# # # ]
946 [ # # # # : 252 : target->total_size = spdk_bdev_get_num_blocks(target->u.bdev.bdev) * target->block_size;
# # # # #
# # # # #
# # ]
947 : :
948 [ - + + + : 252 : g_opts.queue_depth = spdk_min(g_opts.queue_depth,
- + # # #
# # # # #
# # # # #
# # # #
# ]
949 : : (target->total_size / g_opts.io_unit_size) - skip_blocks + 1);
950 : :
951 [ + + # # ]: 252 : if (g_opts.io_unit_count != 0) {
952 [ # # # # : 141 : g_opts.queue_depth = spdk_min(g_opts.queue_depth, g_opts.io_unit_count);
# # # # #
# # # ]
953 : 0 : }
954 : :
955 : 252 : return 0;
956 : 0 : }
957 : :
958 : : static void
959 : 0 : dd_finish(void)
960 : : {
961 : : /* Interrupt operation */
962 : 0 : g_interrupt = true;
963 : 0 : }
964 : :
965 : : static int
966 : 421 : parse_flags(char *file_flags)
967 : : {
968 : : char *input_flag;
969 : 421 : int flags = 0;
970 : : int i;
971 : 421 : bool found = false;
972 : :
973 : : /* Translate input flags to file open flags */
974 [ + + # # : 689 : while ((input_flag = strsep(&file_flags, ","))) {
# # ]
975 [ + + # # : 1387 : for (i = 0; g_flags[i].name != NULL; i++) {
# # # # #
# # # ]
976 [ + + - + : 1380 : if (!strcmp(input_flag, g_flags[i].name)) {
+ + # # #
# # # #
# ]
977 [ # # # # : 268 : flags |= g_flags[i].flag;
# # # # ]
978 : 268 : found = true;
979 : 268 : break;
980 : : }
981 : 0 : }
982 : :
983 [ + + # # ]: 275 : if (found == false) {
984 : 7 : SPDK_ERRLOG("Unknown file flag: %s\n", input_flag);
985 : 7 : dd_exit(-EINVAL);
986 : 7 : return 0;
987 : : }
988 : :
989 : 268 : found = false;
990 : : }
991 : :
992 : 414 : return flags;
993 : 0 : }
994 : :
995 : : #ifdef SPDK_CONFIG_URING
996 : : static bool
997 : 2 : dd_is_blk(int fd)
998 : : {
999 : : struct stat st;
1000 : :
1001 [ - + - + ]: 2 : if (fstat(fd, &st) != 0) {
1002 : 0 : return false;
1003 : : }
1004 : :
1005 : 2 : return S_ISBLK(st.st_mode);
1006 : : }
1007 : :
1008 : : struct dd_uring_init_ctx {
1009 : : unsigned int io_uring_flags;
1010 : : int rc;
1011 : : };
1012 : :
1013 : : static void *
1014 : 94 : dd_uring_init(void *arg)
1015 : : {
1016 : 94 : struct dd_uring_init_ctx *ctx = arg;
1017 : :
1018 : 94 : ctx->rc = io_uring_queue_init(g_opts.queue_depth * 2, &g_job.u.uring.ring, ctx->io_uring_flags);
1019 : 94 : return ctx;
1020 : : }
1021 : :
1022 : : static int
1023 : 94 : dd_register_files(void)
1024 : : {
1025 : : int fds[2];
1026 : 94 : unsigned count = 0;
1027 : :
1028 [ + + ]: 94 : if (g_opts.input_file) {
1029 : 72 : fds[count] = g_job.input.u.uring.fd;
1030 : 72 : g_job.input.u.uring.idx = count;
1031 : 72 : count++;
1032 : : }
1033 : :
1034 [ + + ]: 94 : if (g_opts.output_file) {
1035 : 56 : fds[count] = g_job.output.u.uring.fd;
1036 : 56 : g_job.output.u.uring.idx = count;
1037 : 56 : count++;
1038 : : }
1039 : :
1040 : 94 : return io_uring_register_files(&g_job.u.uring.ring, fds, count);
1041 : :
1042 : : }
1043 : :
1044 : : static int
1045 : 92 : dd_register_buffers(void)
1046 : : {
1047 : : struct iovec *iovs;
1048 : : int i, rc;
1049 : :
1050 : 92 : iovs = calloc(g_opts.queue_depth, sizeof(struct iovec));
1051 [ - + ]: 92 : if (iovs == NULL) {
1052 : 0 : return -ENOMEM;
1053 : : }
1054 : :
1055 [ + + ]: 298 : for (i = 0; i < (int)g_opts.queue_depth; i++) {
1056 : 206 : iovs[i].iov_base = g_job.ios[i].buf;
1057 : 206 : iovs[i].iov_len = g_opts.io_unit_size;
1058 : 206 : g_job.ios[i].idx = i;
1059 : : }
1060 : :
1061 : 92 : rc = io_uring_register_buffers(&g_job.u.uring.ring, iovs, g_opts.queue_depth);
1062 : :
1063 : 92 : free(iovs);
1064 : 92 : return rc;
1065 : : }
1066 : : #endif
1067 : :
1068 : : static void
1069 : 412 : dd_run(void *arg1)
1070 : : {
1071 : : uint64_t write_size;
1072 : : uint32_t i;
1073 : 412 : int rc, flags = 0;
1074 : :
1075 [ + + ]: 412 : if (g_opts.input_file) {
1076 [ + + # # ]: 302 : if (g_opts.input_file_flags) {
1077 [ # # ]: 110 : flags = parse_flags(g_opts.input_file_flags);
1078 : 0 : }
1079 : :
1080 [ + + # # ]: 302 : if (dd_open_file(&g_job.input, g_opts.input_file, flags, g_opts.input_offset, true) < 0) {
1081 [ # # ]: 20 : SPDK_ERRLOG("%s: %s\n", g_opts.input_file, strerror(errno));
1082 [ # # # # ]: 20 : dd_exit(-errno);
1083 : 20 : return;
1084 : : }
1085 [ + - # # ]: 110 : } else if (g_opts.input_bdev) {
1086 [ # # # # ]: 110 : rc = dd_open_bdev(&g_job.input, g_opts.input_bdev, g_opts.input_offset);
1087 [ + + ]: 110 : if (rc < 0) {
1088 [ # # # # ]: 2 : SPDK_ERRLOG("%s: %s\n", g_opts.input_bdev, strerror(-rc));
1089 : 2 : dd_exit(rc);
1090 : 2 : return;
1091 : : }
1092 : 0 : }
1093 : :
1094 [ # # # # : 390 : write_size = g_opts.io_unit_count * g_opts.io_unit_size;
# # ]
1095 [ # # # # : 390 : g_job.input.pos = g_opts.input_offset * g_opts.io_unit_size;
# # ]
1096 : :
1097 : : /* We cannot check write size for input files because /dev/zeros, /dev/random, etc would not work.
1098 : : * We will handle that during copying */
1099 [ + + + + : 390 : if (g_opts.input_bdev && g_job.input.pos > g_job.input.total_size) {
# # # # #
# ]
1100 [ - + # # : 5 : SPDK_ERRLOG("--skip value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in input\n",
# # # # ]
1101 : : g_opts.input_offset, g_job.input.total_size / g_opts.io_unit_size);
1102 : 5 : dd_exit(-ENOSPC);
1103 : 5 : return;
1104 : : }
1105 : :
1106 [ + + + + : 385 : if (g_opts.io_unit_count != 0 && g_opts.input_bdev &&
# # # # #
# ]
1107 [ + + # # ]: 74 : write_size + g_job.input.pos > g_job.input.total_size) {
1108 [ - + # # : 5 : SPDK_ERRLOG("--count value too big (%" PRIu64 ") - only %" PRIu64 " blocks available from input\n",
# # # # #
# ]
1109 : : g_opts.io_unit_count, (g_job.input.total_size - g_job.input.pos) / g_opts.io_unit_size);
1110 : 5 : dd_exit(-ENOSPC);
1111 : 5 : return;
1112 : : }
1113 : :
1114 [ + + # # ]: 380 : if (g_opts.io_unit_count != 0) {
1115 [ # # ]: 139 : g_job.copy_size = write_size;
1116 : 0 : } else {
1117 [ # # # # : 241 : g_job.copy_size = g_job.input.total_size - g_job.input.pos;
# # ]
1118 : : }
1119 : :
1120 [ # # # # : 380 : g_job.output.pos = g_opts.output_offset * g_opts.io_unit_size;
# # # # ]
1121 : :
1122 [ + + # # ]: 380 : if (g_opts.output_file) {
1123 : 236 : flags = 0;
1124 : :
1125 [ + + # # ]: 236 : if (g_opts.output_file_flags) {
1126 [ # # ]: 123 : flags = parse_flags(g_opts.output_file_flags);
1127 : 0 : }
1128 : :
1129 [ + + # # : 236 : if (dd_open_file(&g_job.output, g_opts.output_file, flags, g_opts.output_offset, false) < 0) {
# # ]
1130 [ # # # # ]: 20 : SPDK_ERRLOG("%s: %s\n", g_opts.output_file, strerror(errno));
1131 [ # # # # ]: 20 : dd_exit(-errno);
1132 : 20 : return;
1133 : : }
1134 [ + - # # ]: 144 : } else if (g_opts.output_bdev) {
1135 [ # # # # ]: 144 : rc = dd_open_bdev(&g_job.output, g_opts.output_bdev, g_opts.output_offset);
1136 [ - + ]: 144 : if (rc < 0) {
1137 [ # # # # ]: 0 : SPDK_ERRLOG("%s: %s\n", g_opts.output_bdev, strerror(-rc));
1138 : 0 : dd_exit(rc);
1139 : 0 : return;
1140 : : }
1141 : :
1142 [ + + # # : 144 : if (g_job.output.pos > g_job.output.total_size) {
# # # # #
# ]
1143 [ - + # # : 5 : SPDK_ERRLOG("--seek value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in output\n",
# # # # #
# ]
1144 : : g_opts.output_offset, g_job.output.total_size / g_opts.io_unit_size);
1145 : 5 : dd_exit(-ENOSPC);
1146 : 5 : return;
1147 : : }
1148 : :
1149 [ + + + + : 139 : if (g_opts.io_unit_count != 0 && write_size + g_job.output.pos > g_job.output.total_size) {
# # # # #
# # # #
# ]
1150 [ - + # # : 5 : SPDK_ERRLOG("--count value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in output\n",
# # # # #
# # # #
# ]
1151 : : g_opts.io_unit_count, (g_job.output.total_size - g_job.output.pos) / g_opts.io_unit_size);
1152 : 5 : dd_exit(-ENOSPC);
1153 : 5 : return;
1154 : : }
1155 : 0 : }
1156 : :
1157 [ + + # # : 350 : if ((g_job.output.block_size > g_opts.io_unit_size) ||
# # # # #
# ]
1158 [ - + # # ]: 345 : (g_job.input.block_size > g_opts.io_unit_size)) {
1159 [ # # # # : 5 : SPDK_ERRLOG("--bs value cannot be less than input (%d) neither output (%d) native block size\n",
# # ]
1160 : : g_job.input.block_size, g_job.output.block_size);
1161 : 5 : dd_exit(-EINVAL);
1162 : 5 : return;
1163 : : }
1164 : :
1165 [ + + + + : 345 : if (g_opts.input_bdev && g_opts.io_unit_size % g_job.input.block_size != 0) {
+ + # # #
# # # ]
1166 [ # # ]: 5 : SPDK_ERRLOG("--bs value must be a multiple of input native block size (%d)\n",
1167 : : g_job.input.block_size);
1168 : 5 : dd_exit(-EINVAL);
1169 : 5 : return;
1170 : : }
1171 : :
1172 [ # # # # ]: 340 : g_job.ios = calloc(g_opts.queue_depth, sizeof(struct dd_io));
1173 [ - + # # ]: 340 : if (g_job.ios == NULL) {
1174 : 0 : SPDK_ERRLOG("%s\n", strerror(ENOMEM));
1175 : 0 : dd_exit(-ENOMEM);
1176 : 0 : return;
1177 : : }
1178 : :
1179 [ + + # # ]: 1003 : for (i = 0; i < g_opts.queue_depth; i++) {
1180 [ # # # # : 668 : g_job.ios[i].buf = spdk_malloc(g_opts.io_unit_size, 0x1000, NULL, 0, SPDK_MALLOC_DMA);
# # # # #
# ]
1181 [ + + # # : 668 : if (g_job.ios[i].buf == NULL) {
# # # # #
# ]
1182 : 5 : SPDK_ERRLOG("%s - try smaller block size value\n", strerror(ENOMEM));
1183 : 5 : dd_exit(-ENOMEM);
1184 : 5 : return;
1185 : : }
1186 [ # # # # : 663 : g_job.ios[i].length = (uint64_t)g_opts.io_unit_size;
# # # # #
# ]
1187 : 0 : }
1188 : :
1189 [ + + + + : 335 : if (g_opts.input_file || g_opts.output_file) {
# # ]
1190 : : #ifdef SPDK_CONFIG_URING
1191 [ - + + + ]: 118 : if (g_opts.aio == false) {
1192 : : struct dd_uring_init_ctx ctx;
1193 : 94 : int flags = parse_flags(g_opts.input_file_flags) & parse_flags(g_opts.output_file_flags);
1194 : :
1195 : 94 : ctx.io_uring_flags = IORING_SETUP_SQPOLL;
1196 [ + + - + ]: 96 : if ((flags & O_DIRECT) != 0 &&
1197 [ - - ]: 2 : dd_is_blk(g_job.input.u.uring.fd) &&
1198 : 0 : dd_is_blk(g_job.output.u.uring.fd)) {
1199 : 0 : ctx.io_uring_flags = IORING_SETUP_IOPOLL;
1200 : : }
1201 : :
1202 : 94 : g_job.u.uring.poller = SPDK_POLLER_REGISTER(dd_uring_poll, NULL, 0);
1203 : :
1204 : : /* Initialized uring kernel threads inherit parent process CPU mask, to avoid conflicting
1205 : : * with SPDK cores initialize uring without any affinity. */
1206 [ + - - + ]: 94 : if (spdk_call_unaffinitized(dd_uring_init, &ctx) == NULL || ctx.rc) {
1207 : 0 : SPDK_ERRLOG("Failed to create io_uring: %d (%s)\n", ctx.rc, spdk_strerror(-ctx.rc));
1208 : 0 : dd_exit(ctx.rc);
1209 : 2 : return;
1210 : : }
1211 : 94 : g_job.u.uring.active = true;
1212 : :
1213 : : /* Register the files */
1214 : 94 : rc = dd_register_files();
1215 [ + + ]: 94 : if (rc) {
1216 : 2 : SPDK_ERRLOG("Failed to register files with io_uring: %d (%s)\n", rc, spdk_strerror(-rc));
1217 : 2 : dd_exit(rc);
1218 : 2 : return;
1219 : : }
1220 : :
1221 : : /* Register the buffers */
1222 : 92 : rc = dd_register_buffers();
1223 [ - + ]: 92 : if (rc) {
1224 : 0 : SPDK_ERRLOG("Failed to register buffers with io_uring: %d (%s)\n", rc, spdk_strerror(-rc));
1225 : 0 : dd_exit(rc);
1226 : 0 : return;
1227 : : }
1228 : :
1229 : : } else
1230 : : #endif
1231 : : {
1232 [ # # # # : 219 : g_job.u.aio.poller = SPDK_POLLER_REGISTER(dd_aio_poll, NULL, 0);
# # ]
1233 [ # # # # : 219 : io_setup(g_opts.queue_depth, &g_job.u.aio.io_ctx);
# # ]
1234 : : }
1235 : 0 : }
1236 : :
1237 [ # # ]: 333 : clock_gettime(CLOCK_REALTIME, &g_job.start_time);
1238 : :
1239 [ # # ]: 333 : g_job.status_poller = SPDK_POLLER_REGISTER(dd_status_poller, NULL,
1240 : : STATUS_POLLER_PERIOD_SEC * SPDK_SEC_TO_USEC);
1241 : :
1242 [ # # # # : 333 : STAILQ_INIT(&g_job.seek_queue);
# # # # #
# ]
1243 : :
1244 [ + + # # ]: 994 : for (i = 0; i < g_opts.queue_depth; i++) {
1245 [ # # # # ]: 661 : dd_target_seek(&g_job.ios[i]);
1246 : 0 : }
1247 : :
1248 : 0 : }
1249 : :
1250 : : enum dd_cmdline_opts {
1251 : : DD_OPTION_IF = 0x1000,
1252 : : DD_OPTION_OF,
1253 : : DD_OPTION_IFLAGS,
1254 : : DD_OPTION_OFLAGS,
1255 : : DD_OPTION_IB,
1256 : : DD_OPTION_OB,
1257 : : DD_OPTION_SKIP,
1258 : : DD_OPTION_SEEK,
1259 : : DD_OPTION_BS,
1260 : : DD_OPTION_QD,
1261 : : DD_OPTION_COUNT,
1262 : : DD_OPTION_AIO,
1263 : : DD_OPTION_SPARSE,
1264 : : };
1265 : :
1266 : : static struct option g_cmdline_opts[] = {
1267 : : {
1268 : : .name = "if",
1269 : : .has_arg = 1,
1270 : : .flag = NULL,
1271 : : .val = DD_OPTION_IF,
1272 : : },
1273 : : {
1274 : : .name = "of",
1275 : : .has_arg = 1,
1276 : : .flag = NULL,
1277 : : .val = DD_OPTION_OF,
1278 : : },
1279 : : {
1280 : : .name = "iflag",
1281 : : .has_arg = 1,
1282 : : .flag = NULL,
1283 : : .val = DD_OPTION_IFLAGS,
1284 : : },
1285 : : {
1286 : : .name = "oflag",
1287 : : .has_arg = 1,
1288 : : .flag = NULL,
1289 : : .val = DD_OPTION_OFLAGS,
1290 : : },
1291 : : {
1292 : : .name = "ib",
1293 : : .has_arg = 1,
1294 : : .flag = NULL,
1295 : : .val = DD_OPTION_IB,
1296 : : },
1297 : : {
1298 : : .name = "ob",
1299 : : .has_arg = 1,
1300 : : .flag = NULL,
1301 : : .val = DD_OPTION_OB,
1302 : : },
1303 : : {
1304 : : .name = "skip",
1305 : : .has_arg = 1,
1306 : : .flag = NULL,
1307 : : .val = DD_OPTION_SKIP,
1308 : : },
1309 : : {
1310 : : .name = "seek",
1311 : : .has_arg = 1,
1312 : : .flag = NULL,
1313 : : .val = DD_OPTION_SEEK,
1314 : : },
1315 : : {
1316 : : .name = "bs",
1317 : : .has_arg = 1,
1318 : : .flag = NULL,
1319 : : .val = DD_OPTION_BS,
1320 : : },
1321 : : {
1322 : : .name = "qd",
1323 : : .has_arg = 1,
1324 : : .flag = NULL,
1325 : : .val = DD_OPTION_QD,
1326 : : },
1327 : : {
1328 : : .name = "count",
1329 : : .has_arg = 1,
1330 : : .flag = NULL,
1331 : : .val = DD_OPTION_COUNT,
1332 : : },
1333 : : {
1334 : : .name = "aio",
1335 : : .has_arg = 0,
1336 : : .flag = NULL,
1337 : : .val = DD_OPTION_AIO,
1338 : : },
1339 : : {
1340 : : .name = "sparse",
1341 : : .has_arg = 0,
1342 : : .flag = NULL,
1343 : : .val = DD_OPTION_SPARSE,
1344 : : },
1345 : : {
1346 : : .name = NULL
1347 : : }
1348 : : };
1349 : :
1350 : : static void
1351 : 5 : usage(void)
1352 : : {
1353 [ - + ]: 5 : printf("[--------- DD Options ---------]\n");
1354 [ - + ]: 5 : printf(" --if Input file. Must specify either --if or --ib.\n");
1355 [ - + ]: 5 : printf(" --ib Input bdev. Must specifier either --if or --ib\n");
1356 [ - + ]: 5 : printf(" --of Output file. Must specify either --of or --ob.\n");
1357 [ - + ]: 5 : printf(" --ob Output bdev. Must specify either --of or --ob.\n");
1358 [ - + ]: 5 : printf(" --iflag Input file flags.\n");
1359 [ - + ]: 5 : printf(" --oflag Output file flags.\n");
1360 [ - + ]: 5 : printf(" --bs I/O unit size (default: %" PRId64 ")\n", g_opts.io_unit_size);
1361 [ - + ]: 5 : printf(" --qd Queue depth (default: %d)\n", g_opts.queue_depth);
1362 [ - + ]: 5 : printf(" --count I/O unit count. The number of I/O units to copy. (default: all)\n");
1363 [ - + ]: 5 : printf(" --skip Skip this many I/O units at start of input. (default: 0)\n");
1364 [ - + ]: 5 : printf(" --seek Skip this many I/O units at start of output. (default: 0)\n");
1365 [ - + ]: 5 : printf(" --aio Force usage of AIO. (by default io_uring is used if available)\n");
1366 [ - + ]: 5 : printf(" --sparse Enable hole skipping in input target\n");
1367 [ - + ]: 5 : printf(" Available iflag and oflag values:\n");
1368 [ - + ]: 5 : printf(" append - append mode\n");
1369 [ - + ]: 5 : printf(" direct - use direct I/O for data\n");
1370 [ - + ]: 5 : printf(" directory - fail unless a directory\n");
1371 [ - + ]: 5 : printf(" dsync - use synchronized I/O for data\n");
1372 [ - + ]: 5 : printf(" noatime - do not update access time\n");
1373 [ - + ]: 5 : printf(" noctty - do not assign controlling terminal from file\n");
1374 [ - + ]: 5 : printf(" nofollow - do not follow symlinks\n");
1375 [ - + ]: 5 : printf(" nonblock - use non-blocking I/O\n");
1376 [ - + ]: 5 : printf(" sync - use synchronized I/O for data and metadata\n");
1377 : 5 : }
1378 : :
1379 : : static int
1380 : 1727 : parse_args(int argc, char *argv)
1381 : : {
1382 [ + + + + : 1727 : switch (argc) {
+ + + + +
+ + + +
- ]
1383 : 332 : case DD_OPTION_IF:
1384 [ - + ]: 332 : g_opts.input_file = strdup(argv);
1385 : 332 : break;
1386 : 278 : case DD_OPTION_OF:
1387 [ - + # # ]: 278 : g_opts.output_file = strdup(argv);
1388 : 278 : break;
1389 : 115 : case DD_OPTION_IFLAGS:
1390 [ - + # # ]: 115 : g_opts.input_file_flags = strdup(argv);
1391 : 115 : break;
1392 : 128 : case DD_OPTION_OFLAGS:
1393 [ - + # # ]: 128 : g_opts.output_file_flags = strdup(argv);
1394 : 128 : break;
1395 : 125 : case DD_OPTION_IB:
1396 [ - + # # ]: 125 : g_opts.input_bdev = strdup(argv);
1397 : 125 : break;
1398 : 179 : case DD_OPTION_OB:
1399 [ - + # # ]: 179 : g_opts.output_bdev = strdup(argv);
1400 : 179 : break;
1401 : 28 : case DD_OPTION_SKIP:
1402 [ # # ]: 28 : g_opts.input_offset = spdk_strtol(optarg, 10);
1403 : 28 : break;
1404 : 24 : case DD_OPTION_SEEK:
1405 [ # # ]: 24 : g_opts.output_offset = spdk_strtol(optarg, 10);
1406 : 24 : break;
1407 : 206 : case DD_OPTION_BS:
1408 [ # # ]: 206 : g_opts.io_unit_size = spdk_strtol(optarg, 10);
1409 : 206 : break;
1410 : 68 : case DD_OPTION_QD:
1411 [ # # ]: 68 : g_opts.queue_depth = spdk_strtol(optarg, 10);
1412 : 68 : break;
1413 : 149 : case DD_OPTION_COUNT:
1414 [ # # ]: 149 : g_opts.io_unit_count = spdk_strtol(optarg, 10);
1415 : 149 : break;
1416 : 80 : case DD_OPTION_AIO:
1417 [ # # ]: 80 : g_opts.aio = true;
1418 : 80 : break;
1419 : 15 : case DD_OPTION_SPARSE:
1420 : 15 : g_opts.sparse = true;
1421 : 15 : break;
1422 : 0 : default:
1423 : 0 : usage();
1424 : 0 : return 1;
1425 : : }
1426 : 1727 : return 0;
1427 : 0 : }
1428 : :
1429 : : static void
1430 : 417 : dd_free(void)
1431 : : {
1432 : : uint32_t i;
1433 : :
1434 : 417 : free(g_opts.input_file);
1435 [ # # ]: 417 : free(g_opts.output_file);
1436 [ # # ]: 417 : free(g_opts.input_bdev);
1437 [ # # ]: 417 : free(g_opts.output_bdev);
1438 [ # # ]: 417 : free(g_opts.input_file_flags);
1439 [ # # ]: 417 : free(g_opts.output_file_flags);
1440 : :
1441 : :
1442 [ + + + + : 417 : if (g_job.input.type == DD_TARGET_TYPE_FILE || g_job.output.type == DD_TARGET_TYPE_FILE) {
# # # # ]
1443 : : #ifdef SPDK_CONFIG_URING
1444 [ - + + + ]: 148 : if (g_opts.aio == false) {
1445 [ - + + + ]: 116 : if (g_job.u.uring.active) {
1446 : 94 : io_uring_unregister_files(&g_job.u.uring.ring);
1447 : 94 : io_uring_queue_exit(&g_job.u.uring.ring);
1448 : : }
1449 : : } else
1450 : : #endif
1451 : : {
1452 [ # # # # : 269 : io_destroy(g_job.u.aio.io_ctx);
# # ]
1453 : : }
1454 : 0 : }
1455 : :
1456 [ + + # # ]: 417 : if (g_job.ios) {
1457 [ + + # # ]: 1008 : for (i = 0; i < g_opts.queue_depth; i++) {
1458 [ # # # # : 668 : spdk_free(g_job.ios[i].buf);
# # # # ]
1459 : 0 : }
1460 : :
1461 [ # # ]: 340 : free(g_job.ios);
1462 : 0 : }
1463 : 417 : }
1464 : :
1465 : : int
1466 : 462 : main(int argc, char **argv)
1467 : : {
1468 : 462 : struct spdk_app_opts opts = {};
1469 : 462 : int rc = 1;
1470 : :
1471 : 462 : spdk_app_opts_init(&opts, sizeof(opts));
1472 : 462 : opts.name = "spdk_dd";
1473 : 462 : opts.reactor_mask = "0x1";
1474 : 462 : opts.shutdown_cb = dd_finish;
1475 : 462 : opts.rpc_addr = NULL;
1476 : 462 : rc = spdk_app_parse_args(argc, argv, &opts, "", g_cmdline_opts, parse_args, usage);
1477 [ + + ]: 462 : if (rc == SPDK_APP_PARSE_ARGS_FAIL) {
1478 : 5 : SPDK_ERRLOG("Invalid arguments\n");
1479 : 5 : goto end;
1480 [ - + ]: 457 : } else if (rc == SPDK_APP_PARSE_ARGS_HELP) {
1481 : 0 : goto end;
1482 : : }
1483 : :
1484 [ + + + + : 457 : if (g_opts.input_file != NULL && g_opts.input_bdev != NULL) {
# # ]
1485 : 5 : SPDK_ERRLOG("You may specify either --if or --ib, but not both.\n");
1486 : 5 : rc = EINVAL;
1487 : 5 : goto end;
1488 : : }
1489 : :
1490 [ + + + + : 452 : if (g_opts.output_file != NULL && g_opts.output_bdev != NULL) {
# # # # ]
1491 : 5 : SPDK_ERRLOG("You may specify either --of or --ob, but not both.\n");
1492 : 5 : rc = EINVAL;
1493 : 5 : goto end;
1494 : : }
1495 : :
1496 [ + + + + : 447 : if (g_opts.input_file == NULL && g_opts.input_bdev == NULL) {
# # ]
1497 : 5 : SPDK_ERRLOG("You must specify either --if or --ib\n");
1498 : 5 : rc = EINVAL;
1499 : 5 : goto end;
1500 : : }
1501 : :
1502 [ + + + + : 442 : if (g_opts.output_file == NULL && g_opts.output_bdev == NULL) {
# # # # ]
1503 : 5 : SPDK_ERRLOG("You must specify either --of or --ob\n");
1504 : 5 : rc = EINVAL;
1505 : 5 : goto end;
1506 : : }
1507 : :
1508 [ + + # # ]: 437 : if (g_opts.io_unit_size <= 0) {
1509 : 5 : SPDK_ERRLOG("Invalid --bs value\n");
1510 : 5 : rc = EINVAL;
1511 : 5 : goto end;
1512 : : }
1513 : :
1514 [ + + # # ]: 432 : if (g_opts.io_unit_count < 0) {
1515 : 5 : SPDK_ERRLOG("Invalid --count value\n");
1516 : 5 : rc = EINVAL;
1517 : 5 : goto end;
1518 : : }
1519 : :
1520 [ + + + + : 427 : if (g_opts.output_file == NULL && g_opts.output_file_flags != NULL) {
# # # # ]
1521 : 5 : SPDK_ERRLOG("--oflags may be used only with --of\n");
1522 : 5 : rc = EINVAL;
1523 : 5 : goto end;
1524 : : }
1525 : :
1526 [ + + + + : 422 : if (g_opts.input_file == NULL && g_opts.input_file_flags != NULL) {
# # ]
1527 : 5 : SPDK_ERRLOG("--iflags may be used only with --if\n");
1528 : 5 : rc = EINVAL;
1529 : 5 : goto end;
1530 : : }
1531 : :
1532 : 417 : rc = spdk_app_start(&opts, dd_run, NULL);
1533 [ + + ]: 417 : if (rc) {
1534 : 87 : SPDK_ERRLOG("Error occurred while performing copy\n");
1535 : 0 : }
1536 : :
1537 : 417 : dd_free();
1538 : 417 : spdk_app_fini();
1539 : :
1540 : 462 : end:
1541 : 462 : return rc;
1542 : : }
|