spdk_dd: Add data and hole management
A logic has been introduced to skip hole in files and bdevs and so to copy only data. For files lseek is used, for bdevs new bdev API spdk_bdev_seek_[data,hole]. Show progress function will display only affected size_unit Signed-off-by: Damiano Cipriani <damiano.cipriani@suse.com> Change-Id: Ide2b0d825267603d45e00872ea719c8f0e82a60c Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/14363 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
parent
552a26d9d9
commit
3e917320b9
@ -34,6 +34,7 @@ struct spdk_dd_opts {
|
|||||||
int64_t io_unit_count;
|
int64_t io_unit_count;
|
||||||
uint32_t queue_depth;
|
uint32_t queue_depth;
|
||||||
bool aio;
|
bool aio;
|
||||||
|
bool sparse;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spdk_dd_opts g_opts = {
|
static struct spdk_dd_opts g_opts = {
|
||||||
@ -56,6 +57,7 @@ struct dd_io {
|
|||||||
int idx;
|
int idx;
|
||||||
#endif
|
#endif
|
||||||
void *buf;
|
void *buf;
|
||||||
|
STAILQ_ENTRY(dd_io) link;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum dd_target_type {
|
enum dd_target_type {
|
||||||
@ -118,6 +120,7 @@ struct dd_job {
|
|||||||
|
|
||||||
uint32_t outstanding;
|
uint32_t outstanding;
|
||||||
uint64_t copy_size;
|
uint64_t copy_size;
|
||||||
|
STAILQ_HEAD(, dd_io) seek_queue;
|
||||||
|
|
||||||
struct timespec start_time;
|
struct timespec start_time;
|
||||||
uint64_t total_bytes;
|
uint64_t total_bytes;
|
||||||
@ -147,7 +150,8 @@ static struct dd_job g_job = {};
|
|||||||
static int g_error = 0;
|
static int g_error = 0;
|
||||||
static bool g_interrupt;
|
static bool g_interrupt;
|
||||||
|
|
||||||
static void dd_target_populate_buffer(struct dd_io *io);
|
static void dd_target_seek(struct dd_io *io);
|
||||||
|
static void _dd_bdev_seek_hole_done(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dd_cleanup_bdev(struct dd_target io)
|
dd_cleanup_bdev(struct dd_target io)
|
||||||
@ -278,6 +282,37 @@ dd_status_poller(void *ctx)
|
|||||||
return SPDK_POLLER_BUSY;
|
return SPDK_POLLER_BUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dd_finalize_output(void)
|
||||||
|
{
|
||||||
|
off_t curr_offset;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if (g_job.outstanding > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_opts.output_file) {
|
||||||
|
curr_offset = lseek(g_job.output.u.aio.fd, 0, SEEK_END);
|
||||||
|
if (curr_offset == (off_t) -1) {
|
||||||
|
SPDK_ERRLOG("Could not seek output file for finalize: %s\n", strerror(errno));
|
||||||
|
g_error = errno;
|
||||||
|
} else if ((uint64_t)curr_offset < g_job.copy_size + g_job.output.pos) {
|
||||||
|
rc = ftruncate(g_job.output.u.aio.fd, g_job.copy_size + g_job.output.pos);
|
||||||
|
if (rc != 0) {
|
||||||
|
SPDK_ERRLOG("Could not truncate output file for finalize: %s\n", strerror(errno));
|
||||||
|
g_error = errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_error == 0) {
|
||||||
|
dd_show_progress(true);
|
||||||
|
printf("\n\n");
|
||||||
|
}
|
||||||
|
dd_exit(g_error);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SPDK_CONFIG_URING
|
#ifdef SPDK_CONFIG_URING
|
||||||
static void
|
static void
|
||||||
dd_uring_submit(struct dd_io *io, struct dd_target *target, uint64_t length, uint64_t offset)
|
dd_uring_submit(struct dd_io *io, struct dd_target *target, uint64_t length, uint64_t offset)
|
||||||
@ -306,7 +341,7 @@ _dd_write_bdev_done(struct spdk_bdev_io *bdev_io,
|
|||||||
assert(g_job.outstanding > 0);
|
assert(g_job.outstanding > 0);
|
||||||
g_job.outstanding--;
|
g_job.outstanding--;
|
||||||
spdk_bdev_free_io(bdev_io);
|
spdk_bdev_free_io(bdev_io);
|
||||||
dd_target_populate_buffer(io);
|
dd_target_seek(io);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -454,7 +489,7 @@ dd_target_populate_buffer(struct dd_io *io)
|
|||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
io->offset = g_job.input.pos;
|
io->offset = g_job.input.pos;
|
||||||
io->length = spdk_min((uint64_t)g_opts.io_unit_size, g_job.copy_size - read_offset);
|
io->length = spdk_min(io->length, g_job.copy_size - read_offset);
|
||||||
|
|
||||||
if (io->length == 0 || g_error != 0 || g_interrupt == true) {
|
if (io->length == 0 || g_error != 0 || g_interrupt == true) {
|
||||||
if (g_job.outstanding == 0) {
|
if (g_job.outstanding == 0) {
|
||||||
@ -512,6 +547,235 @@ dd_target_populate_buffer(struct dd_io *io)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static off_t
|
||||||
|
dd_file_seek_data(void)
|
||||||
|
{
|
||||||
|
off_t next_data_offset = (off_t) -1;
|
||||||
|
|
||||||
|
next_data_offset = lseek(g_job.input.u.aio.fd, g_job.input.pos, SEEK_DATA);
|
||||||
|
|
||||||
|
if (next_data_offset == (off_t) -1) {
|
||||||
|
/* NXIO with SEEK_DATA means there are no more data to read.
|
||||||
|
* But in case of input and output files, we may have to finalize output file
|
||||||
|
* inserting a hole to the end of the file.
|
||||||
|
*/
|
||||||
|
if (errno == ENXIO) {
|
||||||
|
dd_finalize_output();
|
||||||
|
} else if (g_job.outstanding == 0) {
|
||||||
|
SPDK_ERRLOG("Could not seek input file for data: %s\n", strerror(errno));
|
||||||
|
g_error = errno;
|
||||||
|
dd_exit(g_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return next_data_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static off_t
|
||||||
|
dd_file_seek_hole(void)
|
||||||
|
{
|
||||||
|
off_t next_hole_offset = (off_t) -1;
|
||||||
|
|
||||||
|
next_hole_offset = lseek(g_job.input.u.aio.fd, g_job.input.pos, SEEK_HOLE);
|
||||||
|
|
||||||
|
if (next_hole_offset == (off_t) -1 && g_job.outstanding == 0) {
|
||||||
|
SPDK_ERRLOG("Could not seek input file for hole: %s\n", strerror(errno));
|
||||||
|
g_error = errno;
|
||||||
|
dd_exit(g_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next_hole_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_dd_bdev_seek_data_done(struct spdk_bdev_io *bdev_io,
|
||||||
|
bool success,
|
||||||
|
void *cb_arg)
|
||||||
|
{
|
||||||
|
struct dd_io *io = cb_arg;
|
||||||
|
uint64_t next_data_offset_blocks = UINT64_MAX;
|
||||||
|
struct dd_target *target = &g_job.input;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if (g_error != 0 || g_interrupt == true) {
|
||||||
|
STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
|
||||||
|
if (g_job.outstanding == 0) {
|
||||||
|
if (g_error == 0) {
|
||||||
|
dd_show_progress(true);
|
||||||
|
printf("\n\n");
|
||||||
|
}
|
||||||
|
dd_exit(g_error);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(g_job.outstanding > 0);
|
||||||
|
g_job.outstanding--;
|
||||||
|
|
||||||
|
next_data_offset_blocks = spdk_bdev_io_get_seek_offset(bdev_io);
|
||||||
|
spdk_bdev_free_io(bdev_io);
|
||||||
|
|
||||||
|
/* UINT64_MAX means there are no more data to read.
|
||||||
|
* But in case of input and output files, we may have to finalize output file
|
||||||
|
* inserting a hole to the end of the file.
|
||||||
|
*/
|
||||||
|
if (next_data_offset_blocks == UINT64_MAX) {
|
||||||
|
STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
|
||||||
|
dd_finalize_output();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_job.input.pos = next_data_offset_blocks * g_job.input.block_size;
|
||||||
|
|
||||||
|
g_job.outstanding++;
|
||||||
|
rc = spdk_bdev_seek_hole(target->u.bdev.desc, target->u.bdev.ch,
|
||||||
|
g_job.input.pos / g_job.input.block_size,
|
||||||
|
_dd_bdev_seek_hole_done, io);
|
||||||
|
|
||||||
|
if (rc != 0) {
|
||||||
|
SPDK_ERRLOG("%s\n", strerror(-rc));
|
||||||
|
STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
|
||||||
|
assert(g_job.outstanding > 0);
|
||||||
|
g_job.outstanding--;
|
||||||
|
g_error = rc;
|
||||||
|
if (g_job.outstanding == 0) {
|
||||||
|
dd_exit(rc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_dd_bdev_seek_hole_done(struct spdk_bdev_io *bdev_io,
|
||||||
|
bool success,
|
||||||
|
void *cb_arg)
|
||||||
|
{
|
||||||
|
struct dd_io *io = cb_arg;
|
||||||
|
struct dd_target *target = &g_job.input;
|
||||||
|
uint64_t next_hole_offset_blocks = UINT64_MAX;
|
||||||
|
struct dd_io *seek_io;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
/* First seek operation is the one in progress, i.e. this one just ended */
|
||||||
|
STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
|
||||||
|
|
||||||
|
if (g_error != 0 || g_interrupt == true) {
|
||||||
|
if (g_job.outstanding == 0) {
|
||||||
|
if (g_error == 0) {
|
||||||
|
dd_show_progress(true);
|
||||||
|
printf("\n\n");
|
||||||
|
}
|
||||||
|
dd_exit(g_error);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(g_job.outstanding > 0);
|
||||||
|
g_job.outstanding--;
|
||||||
|
|
||||||
|
next_hole_offset_blocks = spdk_bdev_io_get_seek_offset(bdev_io);
|
||||||
|
spdk_bdev_free_io(bdev_io);
|
||||||
|
|
||||||
|
/* UINT64_MAX means there are no more holes. */
|
||||||
|
if (next_hole_offset_blocks == UINT64_MAX) {
|
||||||
|
io->length = g_opts.io_unit_size;
|
||||||
|
} else {
|
||||||
|
io->length = spdk_min((uint64_t)g_opts.io_unit_size,
|
||||||
|
next_hole_offset_blocks * g_job.input.block_size - g_job.input.pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
dd_target_populate_buffer(io);
|
||||||
|
|
||||||
|
/* If input reading is not at the end, start following seek operation in the queue */
|
||||||
|
if (!STAILQ_EMPTY(&g_job.seek_queue) && g_job.input.pos < g_job.input.total_size) {
|
||||||
|
seek_io = STAILQ_FIRST(&g_job.seek_queue);
|
||||||
|
assert(seek_io != NULL);
|
||||||
|
g_job.outstanding++;
|
||||||
|
rc = spdk_bdev_seek_data(target->u.bdev.desc, target->u.bdev.ch,
|
||||||
|
g_job.input.pos / g_job.input.block_size,
|
||||||
|
_dd_bdev_seek_data_done, seek_io);
|
||||||
|
|
||||||
|
if (rc != 0) {
|
||||||
|
SPDK_ERRLOG("%s\n", strerror(-rc));
|
||||||
|
assert(g_job.outstanding > 0);
|
||||||
|
g_job.outstanding--;
|
||||||
|
g_error = rc;
|
||||||
|
if (g_job.outstanding == 0) {
|
||||||
|
dd_exit(rc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dd_target_seek(struct dd_io *io)
|
||||||
|
{
|
||||||
|
struct dd_target *target = &g_job.input;
|
||||||
|
uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size;
|
||||||
|
uint64_t read_offset = g_job.input.pos - read_region_start;
|
||||||
|
off_t next_data_offset = (off_t) -1;
|
||||||
|
off_t next_hole_offset = (off_t) -1;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if (!g_opts.sparse) {
|
||||||
|
dd_target_populate_buffer(io);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_job.copy_size - read_offset == 0 || g_error != 0 || g_interrupt == true) {
|
||||||
|
if (g_job.outstanding == 0) {
|
||||||
|
if (g_error == 0) {
|
||||||
|
dd_show_progress(true);
|
||||||
|
printf("\n\n");
|
||||||
|
}
|
||||||
|
dd_exit(g_error);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target->type == DD_TARGET_TYPE_FILE) {
|
||||||
|
next_data_offset = dd_file_seek_data();
|
||||||
|
if (next_data_offset < 0) {
|
||||||
|
return;
|
||||||
|
} else if ((uint64_t)next_data_offset > g_job.input.pos) {
|
||||||
|
g_job.input.pos = next_data_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_hole_offset = dd_file_seek_hole();
|
||||||
|
if (next_hole_offset < 0) {
|
||||||
|
return;
|
||||||
|
} else if ((uint64_t)next_hole_offset > g_job.input.pos) {
|
||||||
|
io->length = spdk_min((uint64_t)g_opts.io_unit_size,
|
||||||
|
(uint64_t)(next_hole_offset - g_job.input.pos));
|
||||||
|
} else {
|
||||||
|
io->length = g_opts.io_unit_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd_target_populate_buffer(io);
|
||||||
|
} else if (target->type == DD_TARGET_TYPE_BDEV) {
|
||||||
|
/* Check if other seek operation is in progress */
|
||||||
|
if (STAILQ_EMPTY(&g_job.seek_queue)) {
|
||||||
|
g_job.outstanding++;
|
||||||
|
rc = spdk_bdev_seek_data(target->u.bdev.desc, target->u.bdev.ch,
|
||||||
|
g_job.input.pos / g_job.input.block_size,
|
||||||
|
_dd_bdev_seek_data_done, io);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
STAILQ_INSERT_TAIL(&g_job.seek_queue, io, link);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc != 0) {
|
||||||
|
SPDK_ERRLOG("%s\n", strerror(-rc));
|
||||||
|
assert(g_job.outstanding > 0);
|
||||||
|
g_job.outstanding--;
|
||||||
|
g_error = rc;
|
||||||
|
if (g_job.outstanding == 0) {
|
||||||
|
dd_exit(rc);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dd_complete_poll(struct dd_io *io)
|
dd_complete_poll(struct dd_io *io)
|
||||||
{
|
{
|
||||||
@ -526,7 +790,7 @@ dd_complete_poll(struct dd_io *io)
|
|||||||
dd_target_write(io);
|
dd_target_write(io);
|
||||||
break;
|
break;
|
||||||
case DD_WRITE:
|
case DD_WRITE:
|
||||||
dd_target_populate_buffer(io);
|
dd_target_seek(io);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
@ -905,6 +1169,7 @@ dd_run(void *arg1)
|
|||||||
dd_exit(-ENOMEM);
|
dd_exit(-ENOMEM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
g_job.ios[i].length = (uint64_t)g_opts.io_unit_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_opts.input_file || g_opts.output_file) {
|
if (g_opts.input_file || g_opts.output_file) {
|
||||||
@ -957,8 +1222,10 @@ dd_run(void *arg1)
|
|||||||
g_job.status_poller = spdk_poller_register(dd_status_poller, NULL,
|
g_job.status_poller = spdk_poller_register(dd_status_poller, NULL,
|
||||||
STATUS_POLLER_PERIOD_SEC * SPDK_SEC_TO_USEC);
|
STATUS_POLLER_PERIOD_SEC * SPDK_SEC_TO_USEC);
|
||||||
|
|
||||||
|
STAILQ_INIT(&g_job.seek_queue);
|
||||||
|
|
||||||
for (i = 0; i < g_opts.queue_depth; i++) {
|
for (i = 0; i < g_opts.queue_depth; i++) {
|
||||||
dd_target_populate_buffer(&g_job.ios[i]);
|
dd_target_seek(&g_job.ios[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -976,6 +1243,7 @@ enum dd_cmdline_opts {
|
|||||||
DD_OPTION_QD,
|
DD_OPTION_QD,
|
||||||
DD_OPTION_COUNT,
|
DD_OPTION_COUNT,
|
||||||
DD_OPTION_AIO,
|
DD_OPTION_AIO,
|
||||||
|
DD_OPTION_SPARSE,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct option g_cmdline_opts[] = {
|
static struct option g_cmdline_opts[] = {
|
||||||
@ -1051,6 +1319,12 @@ static struct option g_cmdline_opts[] = {
|
|||||||
.flag = NULL,
|
.flag = NULL,
|
||||||
.val = DD_OPTION_AIO,
|
.val = DD_OPTION_AIO,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "sparse",
|
||||||
|
.has_arg = 0,
|
||||||
|
.flag = NULL,
|
||||||
|
.val = DD_OPTION_SPARSE,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.name = NULL
|
.name = NULL
|
||||||
}
|
}
|
||||||
@ -1072,6 +1346,7 @@ usage(void)
|
|||||||
printf(" --skip Skip this many I/O units at start of input. (default: 0)\n");
|
printf(" --skip Skip this many I/O units at start of input. (default: 0)\n");
|
||||||
printf(" --seek Skip this many I/O units at start of output. (default: 0)\n");
|
printf(" --seek Skip this many I/O units at start of output. (default: 0)\n");
|
||||||
printf(" --aio Force usage of AIO. (by default io_uring is used if available)\n");
|
printf(" --aio Force usage of AIO. (by default io_uring is used if available)\n");
|
||||||
|
printf(" --sparse Enable hole skipping in input target\n");
|
||||||
printf(" Available iflag and oflag values:\n");
|
printf(" Available iflag and oflag values:\n");
|
||||||
printf(" append - append mode\n");
|
printf(" append - append mode\n");
|
||||||
printf(" direct - use direct I/O for data\n");
|
printf(" direct - use direct I/O for data\n");
|
||||||
@ -1124,6 +1399,9 @@ parse_args(int argc, char *argv)
|
|||||||
case DD_OPTION_AIO:
|
case DD_OPTION_AIO:
|
||||||
g_opts.aio = true;
|
g_opts.aio = true;
|
||||||
break;
|
break;
|
||||||
|
case DD_OPTION_SPARSE:
|
||||||
|
g_opts.sparse = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage();
|
usage();
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -20,3 +20,4 @@ run_test "spdk_dd_bdev_to_bdev" "$testdir/bdev_to_bdev.sh" "${nvmes[@]}"
|
|||||||
if ((SPDK_TEST_URING == 1)); then
|
if ((SPDK_TEST_URING == 1)); then
|
||||||
run_test "spdk_dd_uring" "$testdir/uring.sh"
|
run_test "spdk_dd_uring" "$testdir/uring.sh"
|
||||||
fi
|
fi
|
||||||
|
run_test "spdk_dd_sparse" "$testdir/sparse.sh"
|
||||||
|
118
test/dd/sparse.sh
Executable file
118
test/dd/sparse.sh
Executable file
@ -0,0 +1,118 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
testdir=$(readlink -f "$(dirname "$0")")
|
||||||
|
rootdir=$(readlink -f "$testdir/../../")
|
||||||
|
source "$testdir/common.sh"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm $aio_disk
|
||||||
|
rm $file1
|
||||||
|
rm $file2
|
||||||
|
rm $file3
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
truncate $aio_disk --size 104857600
|
||||||
|
|
||||||
|
dd if=/dev/zero of=$file1 bs=4M count=1
|
||||||
|
dd if=/dev/zero of=$file1 bs=4M count=1 seek=4
|
||||||
|
dd if=/dev/zero of=$file1 bs=4M count=1 seek=8
|
||||||
|
}
|
||||||
|
|
||||||
|
file_to_file() {
|
||||||
|
local stat1_s stat1_b
|
||||||
|
local stat2_s stat2_b
|
||||||
|
|
||||||
|
local -A method_bdev_aio_create_0=(
|
||||||
|
["filename"]=$aio_disk
|
||||||
|
["name"]=$aio_bdev
|
||||||
|
["block_size"]=4096
|
||||||
|
)
|
||||||
|
|
||||||
|
local -A method_bdev_lvol_create_lvstore_1=(
|
||||||
|
["bdev_name"]=$aio_bdev
|
||||||
|
["lvs_name"]=$lvstore
|
||||||
|
)
|
||||||
|
|
||||||
|
"${DD_APP[@]}" \
|
||||||
|
--if="$file1" \
|
||||||
|
--of="$file2" \
|
||||||
|
--bs=12582912 \
|
||||||
|
--sparse \
|
||||||
|
--json <(gen_conf)
|
||||||
|
|
||||||
|
stat1_s=$(stat --printf='%s' $file1)
|
||||||
|
stat2_s=$(stat --printf='%s' $file2)
|
||||||
|
|
||||||
|
[[ $stat1_s == "$stat2_s" ]]
|
||||||
|
|
||||||
|
stat1_b=$(stat --printf='%b' $file1)
|
||||||
|
stat2_b=$(stat --printf='%b' $file2)
|
||||||
|
|
||||||
|
[[ $stat1_b == "$stat2_b" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
file_to_bdev() {
|
||||||
|
local -A method_bdev_aio_create_0=(
|
||||||
|
["filename"]=$aio_disk
|
||||||
|
["name"]=$aio_bdev
|
||||||
|
["block_size"]=4096
|
||||||
|
)
|
||||||
|
|
||||||
|
local -A method_bdev_lvol_create_1=(
|
||||||
|
["lvs_name"]=$lvstore
|
||||||
|
["lvol_name"]=$lvol
|
||||||
|
["size"]=37748736
|
||||||
|
["thin_provision"]=true
|
||||||
|
)
|
||||||
|
|
||||||
|
"${DD_APP[@]}" \
|
||||||
|
--if="$file2" \
|
||||||
|
--ob="$lvstore/$lvol" \
|
||||||
|
--bs=12582912 \
|
||||||
|
--sparse \
|
||||||
|
--json <(gen_conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
bdev_to_file() {
|
||||||
|
local stat2_s stat2_b
|
||||||
|
local stat3_s stat3_b
|
||||||
|
|
||||||
|
local -A method_bdev_aio_create_0=(
|
||||||
|
["filename"]=$aio_disk
|
||||||
|
["name"]=$aio_bdev
|
||||||
|
["block_size"]=4096
|
||||||
|
)
|
||||||
|
|
||||||
|
"${DD_APP[@]}" \
|
||||||
|
--ib="$lvstore/$lvol" \
|
||||||
|
--of="$file3" \
|
||||||
|
--bs=12582912 \
|
||||||
|
--sparse \
|
||||||
|
--json <(gen_conf)
|
||||||
|
|
||||||
|
stat2_s=$(stat --printf='%s' $file2)
|
||||||
|
stat3_s=$(stat --printf='%s' $file3)
|
||||||
|
|
||||||
|
[[ $stat2_s == "$stat3_s" ]]
|
||||||
|
|
||||||
|
stat2_b=$(stat --printf='%b' $file2)
|
||||||
|
stat3_b=$(stat --printf='%b' $file3)
|
||||||
|
|
||||||
|
[[ $stat2_b == "$stat3_b" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
aio_disk="dd_sparse_aio_disk"
|
||||||
|
aio_bdev="dd_aio"
|
||||||
|
file1="file_zero1"
|
||||||
|
file2="file_zero2"
|
||||||
|
file3="file_zero3"
|
||||||
|
lvstore="dd_lvstore"
|
||||||
|
lvol="dd_lvol"
|
||||||
|
|
||||||
|
trap "cleanup" EXIT
|
||||||
|
|
||||||
|
prepare
|
||||||
|
|
||||||
|
run_test "dd_sparse_file_to_file" file_to_file
|
||||||
|
run_test "dd_sparse_file_to_bdev" file_to_bdev
|
||||||
|
run_test "dd_sparse_bdev_to_file" bdev_to_file
|
Loading…
Reference in New Issue
Block a user