2016-07-20 11:16:23 -07:00
|
|
|
/*-
|
|
|
|
* BSD LICENSE
|
|
|
|
*
|
|
|
|
* Copyright (c) Intel Corporation.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
*
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in
|
|
|
|
* the documentation and/or other materials provided with the
|
|
|
|
* distribution.
|
|
|
|
* * Neither the name of Intel Corporation nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2017-05-02 11:18:25 -07:00
|
|
|
#include "spdk/stdinc.h"
|
2016-07-20 11:16:23 -07:00
|
|
|
|
|
|
|
#include "spdk/bdev.h"
|
|
|
|
#include "spdk/copy_engine.h"
|
2016-08-22 15:23:38 -07:00
|
|
|
#include "spdk/endian.h"
|
2016-08-17 13:35:18 -07:00
|
|
|
#include "spdk/env.h"
|
2016-10-05 10:37:11 -07:00
|
|
|
#include "spdk/event.h"
|
2016-07-20 11:16:23 -07:00
|
|
|
#include "spdk/log.h"
|
2017-05-09 10:57:32 -07:00
|
|
|
#include "spdk/util.h"
|
2018-06-11 13:32:15 -07:00
|
|
|
#include "spdk/thread.h"
|
2017-11-02 12:40:01 +08:00
|
|
|
#include "spdk/string.h"
|
2016-07-20 11:16:23 -07:00
|
|
|
|
|
|
|
struct bdevperf_task {
|
2017-01-18 11:28:17 -07:00
|
|
|
struct iovec iov;
|
|
|
|
struct io_target *target;
|
|
|
|
void *buf;
|
2017-09-08 09:12:27 -07:00
|
|
|
uint64_t offset_blocks;
|
2018-06-14 02:03:52 -07:00
|
|
|
enum spdk_bdev_io_type io_type;
|
2017-11-20 18:22:45 +08:00
|
|
|
TAILQ_ENTRY(bdevperf_task) link;
|
2018-06-12 08:11:31 -07:00
|
|
|
struct spdk_bdev_io_wait_entry bdev_io_wait;
|
2016-07-20 11:16:23 -07:00
|
|
|
};
|
|
|
|
|
2018-08-09 13:54:39 +02:00
|
|
|
static const char *g_workload_type;
|
2016-07-20 11:16:23 -07:00
|
|
|
static int g_io_size = 0;
|
2019-02-11 08:28:02 +09:00
|
|
|
static uint64_t g_buf_size = 0;
|
2016-07-20 11:16:23 -07:00
|
|
|
/* initialize to invalid value so we can detect if user overrides it. */
|
|
|
|
static int g_rw_percentage = -1;
|
|
|
|
static int g_is_random;
|
|
|
|
static bool g_verify = false;
|
|
|
|
static bool g_reset = false;
|
|
|
|
static bool g_unmap = false;
|
2018-08-09 03:11:09 -04:00
|
|
|
static bool g_write_zeroes = false;
|
2018-06-07 09:29:50 +08:00
|
|
|
static bool g_flush = false;
|
2016-07-20 11:16:23 -07:00
|
|
|
static int g_queue_depth;
|
2017-12-07 17:21:31 +08:00
|
|
|
static uint64_t g_time_in_usec;
|
2016-07-20 11:16:23 -07:00
|
|
|
static int g_show_performance_real_time = 0;
|
2017-12-13 05:27:17 +08:00
|
|
|
static uint64_t g_show_performance_period_in_usec = 1000000;
|
|
|
|
static uint64_t g_show_performance_period_num = 0;
|
2018-03-06 09:13:05 +09:00
|
|
|
static uint64_t g_show_performance_ema_period = 0;
|
2016-07-20 11:16:23 -07:00
|
|
|
static bool g_run_failed = false;
|
2017-12-07 17:21:31 +08:00
|
|
|
static bool g_shutdown = false;
|
|
|
|
static uint64_t g_shutdown_tsc;
|
2016-07-20 11:16:23 -07:00
|
|
|
static bool g_zcopy = true;
|
2017-11-20 18:22:45 +08:00
|
|
|
static unsigned g_master_core;
|
2018-08-09 13:54:39 +02:00
|
|
|
static int g_time_in_sec;
|
|
|
|
static bool g_mix_specified;
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2016-10-05 10:37:11 -07:00
|
|
|
static struct spdk_poller *g_perf_timer = NULL;
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2018-01-05 15:10:18 -07:00
|
|
|
static void bdevperf_submit_single(struct io_target *target, struct bdevperf_task *task);
|
2016-07-20 11:16:23 -07:00
|
|
|
|
|
|
|
struct io_target {
|
2017-11-20 18:22:45 +08:00
|
|
|
char *name;
|
|
|
|
struct spdk_bdev *bdev;
|
|
|
|
struct spdk_bdev_desc *bdev_desc;
|
|
|
|
struct spdk_io_channel *ch;
|
|
|
|
struct io_target *next;
|
|
|
|
unsigned lcore;
|
2017-12-18 15:15:32 +08:00
|
|
|
uint64_t io_completed;
|
2018-03-06 09:13:05 +09:00
|
|
|
uint64_t prev_io_completed;
|
|
|
|
double ema_io_per_second;
|
2017-11-20 18:22:45 +08:00
|
|
|
int current_queue_depth;
|
|
|
|
uint64_t size_in_ios;
|
|
|
|
uint64_t offset_in_ios;
|
|
|
|
uint64_t io_size_blocks;
|
2019-02-11 08:40:32 +09:00
|
|
|
uint32_t dif_check_flags;
|
2017-11-20 18:22:45 +08:00
|
|
|
bool is_draining;
|
|
|
|
struct spdk_poller *run_timer;
|
|
|
|
struct spdk_poller *reset_timer;
|
|
|
|
TAILQ_HEAD(, bdevperf_task) task_list;
|
2016-07-20 11:16:23 -07:00
|
|
|
};
|
|
|
|
|
2018-07-11 21:13:39 -07:00
|
|
|
struct io_target **g_head;
|
2019-02-11 07:38:35 +09:00
|
|
|
uint32_t *g_coremap;
|
2016-07-20 11:16:23 -07:00
|
|
|
static int g_target_count = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Used to determine how the I/O buffers should be aligned.
|
|
|
|
* This alignment will be bumped up for blockdevs that
|
|
|
|
* require alignment based on block length - for example,
|
|
|
|
* AIO blockdevs.
|
|
|
|
*/
|
2017-05-09 10:57:32 -07:00
|
|
|
static size_t g_min_alignment = 8;
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2019-02-11 08:28:02 +09:00
|
|
|
static void
|
|
|
|
generate_data(void *buf, int buf_len, int block_size, int md_size,
|
|
|
|
int num_blocks, int seed)
|
|
|
|
{
|
|
|
|
int offset_blocks = 0;
|
|
|
|
|
|
|
|
if (buf_len < num_blocks * block_size) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (offset_blocks < num_blocks) {
|
|
|
|
memset(buf, seed, block_size - md_size);
|
|
|
|
memset(buf + block_size - md_size, 0, md_size);
|
|
|
|
buf += block_size;
|
|
|
|
offset_blocks++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
verify_data(void *wr_buf, int wr_buf_len, void *rd_buf, int rd_buf_len,
|
|
|
|
int block_size, int md_size, int num_blocks)
|
|
|
|
{
|
|
|
|
int offset_blocks = 0;
|
|
|
|
|
|
|
|
if (wr_buf_len < num_blocks * block_size || rd_buf_len < num_blocks * block_size) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (offset_blocks < num_blocks) {
|
|
|
|
if (memcmp(wr_buf, rd_buf, block_size - md_size) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
wr_buf += block_size;
|
|
|
|
rd_buf += block_size;
|
|
|
|
offset_blocks++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-12-26 19:48:59 +08:00
|
|
|
static int
|
2016-07-20 11:16:23 -07:00
|
|
|
blockdev_heads_init(void)
|
|
|
|
{
|
2017-12-26 19:48:59 +08:00
|
|
|
uint32_t i, idx = 0;
|
|
|
|
uint32_t core_count = spdk_env_get_core_count();
|
|
|
|
|
2018-07-11 21:13:39 -07:00
|
|
|
g_head = calloc(core_count, sizeof(struct io_target *));
|
|
|
|
if (!g_head) {
|
|
|
|
fprintf(stderr, "Cannot allocate g_head array with size=%u\n",
|
2017-12-26 19:48:59 +08:00
|
|
|
core_count);
|
|
|
|
return -1;
|
|
|
|
}
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2019-02-11 07:38:35 +09:00
|
|
|
g_coremap = calloc(core_count, sizeof(uint32_t));
|
|
|
|
if (!g_coremap) {
|
2018-07-11 21:13:39 -07:00
|
|
|
free(g_head);
|
2017-12-26 19:48:59 +08:00
|
|
|
fprintf(stderr, "Cannot allocate coremap array with size=%u\n",
|
|
|
|
core_count);
|
|
|
|
return -1;
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
2017-08-16 14:37:24 -07:00
|
|
|
|
|
|
|
SPDK_ENV_FOREACH_CORE(i) {
|
2019-02-11 07:38:35 +09:00
|
|
|
g_coremap[idx++] = i;
|
2017-08-16 14:37:24 -07:00
|
|
|
}
|
2017-12-26 19:48:59 +08:00
|
|
|
|
|
|
|
return 0;
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
|
2017-11-20 18:22:45 +08:00
|
|
|
static void
|
|
|
|
bdevperf_free_target(struct io_target *target)
|
|
|
|
{
|
|
|
|
struct bdevperf_task *task, *tmp;
|
|
|
|
|
|
|
|
TAILQ_FOREACH_SAFE(task, &target->task_list, link, tmp) {
|
|
|
|
TAILQ_REMOVE(&target->task_list, task, link);
|
|
|
|
spdk_dma_free(task->buf);
|
2017-11-22 16:12:34 +08:00
|
|
|
free(task);
|
2017-11-20 18:22:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
free(target->name);
|
|
|
|
free(target);
|
|
|
|
}
|
|
|
|
|
2017-11-02 12:40:01 +08:00
|
|
|
static void
|
|
|
|
blockdev_heads_destroy(void)
|
|
|
|
{
|
2017-12-26 19:48:59 +08:00
|
|
|
uint32_t i, core_count;
|
2017-11-02 12:40:01 +08:00
|
|
|
struct io_target *target, *next_target;
|
|
|
|
|
2018-07-11 21:13:39 -07:00
|
|
|
if (!g_head) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-26 19:48:59 +08:00
|
|
|
core_count = spdk_env_get_core_count();
|
|
|
|
for (i = 0; i < core_count; i++) {
|
2018-07-11 21:13:39 -07:00
|
|
|
target = g_head[i];
|
2017-11-02 12:40:01 +08:00
|
|
|
while (target != NULL) {
|
|
|
|
next_target = target->next;
|
2017-11-20 18:22:45 +08:00
|
|
|
bdevperf_free_target(target);
|
2017-11-02 12:40:01 +08:00
|
|
|
target = next_target;
|
|
|
|
}
|
|
|
|
}
|
2017-12-26 19:48:59 +08:00
|
|
|
|
2018-07-11 21:13:39 -07:00
|
|
|
free(g_head);
|
2019-02-11 07:38:35 +09:00
|
|
|
free(g_coremap);
|
2017-11-02 12:40:01 +08:00
|
|
|
}
|
|
|
|
|
2019-02-11 07:55:42 +09:00
|
|
|
static int
|
|
|
|
bdevperf_construct_target(struct spdk_bdev *bdev, struct io_target **_target)
|
2016-07-20 11:16:23 -07:00
|
|
|
{
|
|
|
|
struct io_target *target;
|
2017-05-09 10:57:32 -07:00
|
|
|
size_t align;
|
2019-02-11 08:28:02 +09:00
|
|
|
int block_size, md_size, data_block_size;
|
2017-06-29 11:23:50 -07:00
|
|
|
int rc;
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2019-02-11 07:55:42 +09:00
|
|
|
*_target = NULL;
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2019-02-11 07:55:42 +09:00
|
|
|
if (g_unmap && !spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP)) {
|
|
|
|
printf("Skipping %s because it does not support unmap\n", spdk_bdev_get_name(bdev));
|
|
|
|
return 0;
|
|
|
|
}
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2019-02-11 07:55:42 +09:00
|
|
|
target = malloc(sizeof(struct io_target));
|
|
|
|
if (!target) {
|
|
|
|
fprintf(stderr, "Unable to allocate memory for new target.\n");
|
|
|
|
/* Return immediately because all mallocs will presumably fail after this */
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2017-06-29 11:23:50 -07:00
|
|
|
|
2019-02-11 07:55:42 +09:00
|
|
|
target->name = strdup(spdk_bdev_get_name(bdev));
|
|
|
|
if (!target->name) {
|
|
|
|
fprintf(stderr, "Unable to allocate memory for target name.\n");
|
|
|
|
free(target);
|
|
|
|
/* Return immediately because all mallocs will presumably fail after this */
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2017-11-02 12:40:01 +08:00
|
|
|
|
2019-02-11 07:55:42 +09:00
|
|
|
rc = spdk_bdev_open(bdev, true, NULL, NULL, &target->bdev_desc);
|
|
|
|
if (rc != 0) {
|
|
|
|
SPDK_ERRLOG("Could not open leaf bdev %s, error=%d\n", spdk_bdev_get_name(bdev), rc);
|
|
|
|
free(target->name);
|
|
|
|
free(target);
|
|
|
|
return 0;
|
|
|
|
}
|
2017-06-29 11:23:50 -07:00
|
|
|
|
2019-02-11 07:55:42 +09:00
|
|
|
target->bdev = bdev;
|
|
|
|
target->io_completed = 0;
|
|
|
|
target->current_queue_depth = 0;
|
|
|
|
target->offset_in_ios = 0;
|
2019-02-11 08:28:02 +09:00
|
|
|
|
|
|
|
block_size = spdk_bdev_get_block_size(bdev);
|
|
|
|
md_size = spdk_bdev_get_md_size(bdev);
|
|
|
|
if (md_size != 0 && !spdk_bdev_is_md_interleaved(bdev)) {
|
|
|
|
SPDK_ERRLOG("Separate metadata is not expected.\n");
|
|
|
|
free(target->name);
|
|
|
|
free(target);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
data_block_size = block_size - md_size;
|
|
|
|
target->io_size_blocks = g_io_size / data_block_size;
|
2019-02-13 08:27:26 +09:00
|
|
|
if ((g_io_size % data_block_size) != 0) {
|
|
|
|
SPDK_ERRLOG("IO size (%d) is not multiples of data block size of bdev %s (%"PRIu32")\n",
|
2019-02-11 08:28:02 +09:00
|
|
|
g_io_size, spdk_bdev_get_name(bdev), data_block_size);
|
2019-02-11 07:55:42 +09:00
|
|
|
spdk_bdev_close(target->bdev_desc);
|
|
|
|
free(target->name);
|
|
|
|
free(target);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-02-11 08:28:02 +09:00
|
|
|
g_buf_size = spdk_max(g_buf_size, target->io_size_blocks * block_size);
|
|
|
|
|
2019-02-11 08:40:32 +09:00
|
|
|
target->dif_check_flags = 0;
|
|
|
|
if (spdk_bdev_is_dif_check_enabled(bdev, SPDK_DIF_CHECK_TYPE_REFTAG)) {
|
|
|
|
target->dif_check_flags |= SPDK_DIF_FLAGS_REFTAG_CHECK;
|
|
|
|
}
|
|
|
|
if (spdk_bdev_is_dif_check_enabled(bdev, SPDK_DIF_CHECK_TYPE_GUARD)) {
|
|
|
|
target->dif_check_flags |= SPDK_DIF_FLAGS_GUARD_CHECK;
|
|
|
|
}
|
|
|
|
|
2019-02-11 07:55:42 +09:00
|
|
|
target->size_in_ios = spdk_bdev_get_num_blocks(bdev) / target->io_size_blocks;
|
|
|
|
align = spdk_bdev_get_buf_align(bdev);
|
|
|
|
/*
|
|
|
|
* TODO: This should actually use the LCM of align and g_min_alignment, but
|
|
|
|
* it is fairly safe to assume all alignments are powers of two for now.
|
|
|
|
*/
|
|
|
|
g_min_alignment = spdk_max(g_min_alignment, align);
|
2018-02-06 14:40:41 +01:00
|
|
|
|
2019-02-11 07:55:42 +09:00
|
|
|
target->is_draining = false;
|
|
|
|
target->run_timer = NULL;
|
|
|
|
target->reset_timer = NULL;
|
|
|
|
TAILQ_INIT(&target->task_list);
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2019-02-11 07:55:42 +09:00
|
|
|
*_target = target;
|
|
|
|
return 0;
|
|
|
|
}
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2019-02-11 07:55:42 +09:00
|
|
|
static void
|
|
|
|
bdevperf_construct_targets(void)
|
|
|
|
{
|
|
|
|
int index = 0;
|
|
|
|
struct spdk_bdev *bdev;
|
|
|
|
struct io_target *target;
|
|
|
|
int rc;
|
2016-08-01 14:31:02 -07:00
|
|
|
|
2019-02-11 07:55:42 +09:00
|
|
|
bdev = spdk_bdev_first_leaf();
|
|
|
|
while (bdev != NULL) {
|
|
|
|
rc = bdevperf_construct_target(bdev, &target);
|
|
|
|
if (rc != 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (target != NULL) {
|
|
|
|
/* Mapping each created target to lcore */
|
|
|
|
index = g_target_count % spdk_env_get_core_count();
|
|
|
|
target->next = g_head[index];
|
|
|
|
target->lcore = g_coremap[index];
|
|
|
|
g_head[index] = target;
|
|
|
|
g_target_count++;
|
|
|
|
}
|
2017-06-29 13:16:26 -07:00
|
|
|
bdev = spdk_bdev_next_leaf(bdev);
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-01-04 18:19:02 -07:00
|
|
|
end_run(void *arg1, void *arg2)
|
2016-07-20 11:16:23 -07:00
|
|
|
{
|
2017-01-05 12:44:24 -07:00
|
|
|
struct io_target *target = arg1;
|
|
|
|
|
|
|
|
spdk_put_io_channel(target->ch);
|
2017-06-29 11:23:50 -07:00
|
|
|
spdk_bdev_close(target->bdev_desc);
|
2016-07-20 11:16:23 -07:00
|
|
|
if (--g_target_count == 0) {
|
|
|
|
if (g_show_performance_real_time) {
|
2017-11-15 15:09:40 -07:00
|
|
|
spdk_poller_unregister(&g_perf_timer);
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
2016-10-11 08:34:53 -07:00
|
|
|
if (g_run_failed) {
|
|
|
|
spdk_app_stop(1);
|
|
|
|
} else {
|
|
|
|
spdk_app_stop(0);
|
|
|
|
}
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-05-16 13:25:03 -07:00
|
|
|
bdevperf_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
|
2016-07-20 11:16:23 -07:00
|
|
|
{
|
|
|
|
struct io_target *target;
|
2016-10-05 15:00:20 -07:00
|
|
|
struct bdevperf_task *task = cb_arg;
|
2018-03-02 12:49:36 -07:00
|
|
|
struct spdk_event *complete;
|
2017-05-04 13:57:07 -07:00
|
|
|
struct iovec *iovs;
|
|
|
|
int iovcnt;
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2016-10-11 08:34:53 -07:00
|
|
|
target = task->target;
|
|
|
|
|
2017-05-16 13:25:03 -07:00
|
|
|
if (!success) {
|
2016-11-14 09:55:20 -07:00
|
|
|
if (!g_reset) {
|
|
|
|
target->is_draining = true;
|
|
|
|
g_run_failed = true;
|
2017-12-15 18:34:59 +08:00
|
|
|
printf("task offset: %lu on target bdev=%s fails\n",
|
|
|
|
task->offset_blocks, target->name);
|
2016-11-14 09:55:20 -07:00
|
|
|
}
|
2018-05-31 06:16:10 -07:00
|
|
|
} else if (g_verify || g_reset) {
|
2017-05-04 13:57:07 -07:00
|
|
|
spdk_bdev_io_get_iovec(bdev_io, &iovs, &iovcnt);
|
|
|
|
assert(iovcnt == 1);
|
|
|
|
assert(iovs != NULL);
|
2019-02-11 08:28:02 +09:00
|
|
|
if (!verify_data(task->buf, g_buf_size, iovs[0].iov_base, iovs[0].iov_len,
|
|
|
|
spdk_bdev_get_block_size(target->bdev),
|
|
|
|
spdk_bdev_get_md_size(target->bdev),
|
|
|
|
target->io_size_blocks) != 0) {
|
2017-09-08 09:12:27 -07:00
|
|
|
printf("Buffer mismatch! Disk Offset: %lu\n", task->offset_blocks);
|
2016-10-11 08:34:53 -07:00
|
|
|
target->is_draining = true;
|
2016-07-20 11:16:23 -07:00
|
|
|
g_run_failed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
target->current_queue_depth--;
|
2017-12-13 02:04:29 -05:00
|
|
|
|
|
|
|
if (success) {
|
|
|
|
target->io_completed++;
|
|
|
|
}
|
2016-07-20 11:16:23 -07:00
|
|
|
|
|
|
|
spdk_bdev_free_io(bdev_io);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* is_draining indicates when time has expired for the test run
|
|
|
|
* and we are just waiting for the previously submitted I/O
|
|
|
|
* to complete. In this case, do not submit a new I/O to replace
|
|
|
|
* the one just completed.
|
|
|
|
*/
|
|
|
|
if (!target->is_draining) {
|
2018-01-05 15:10:18 -07:00
|
|
|
bdevperf_submit_single(target, task);
|
|
|
|
} else {
|
|
|
|
TAILQ_INSERT_TAIL(&target->task_list, task, link);
|
|
|
|
if (target->current_queue_depth == 0) {
|
|
|
|
complete = spdk_event_allocate(g_master_core, end_run, target, NULL);
|
|
|
|
spdk_event_call(complete);
|
|
|
|
}
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-06-12 08:11:31 -07:00
|
|
|
bdevperf_verify_submit_read(void *cb_arg)
|
2016-07-20 11:16:23 -07:00
|
|
|
{
|
|
|
|
struct io_target *target;
|
2016-10-05 15:00:20 -07:00
|
|
|
struct bdevperf_task *task = cb_arg;
|
2018-05-31 06:16:10 -07:00
|
|
|
int rc;
|
2016-07-20 11:16:23 -07:00
|
|
|
|
|
|
|
target = task->target;
|
|
|
|
|
|
|
|
/* Read the data back in */
|
2017-09-08 09:12:27 -07:00
|
|
|
rc = spdk_bdev_read_blocks(target->bdev_desc, target->ch, NULL, task->offset_blocks,
|
|
|
|
target->io_size_blocks, bdevperf_complete, task);
|
2018-06-12 08:11:31 -07:00
|
|
|
if (rc == -ENOMEM) {
|
|
|
|
task->bdev_io_wait.bdev = target->bdev;
|
|
|
|
task->bdev_io_wait.cb_fn = bdevperf_verify_submit_read;
|
|
|
|
task->bdev_io_wait.cb_arg = task;
|
|
|
|
spdk_bdev_queue_io_wait(target->bdev, target->ch, &task->bdev_io_wait);
|
|
|
|
} else if (rc != 0) {
|
2017-06-05 11:39:38 -07:00
|
|
|
printf("Failed to submit read: %d\n", rc);
|
|
|
|
target->is_draining = true;
|
|
|
|
g_run_failed = true;
|
|
|
|
}
|
2018-06-12 08:11:31 -07:00
|
|
|
}
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2018-06-12 08:11:31 -07:00
|
|
|
static void
|
|
|
|
bdevperf_verify_write_complete(struct spdk_bdev_io *bdev_io, bool success,
|
|
|
|
void *cb_arg)
|
|
|
|
{
|
2018-07-16 21:33:05 +08:00
|
|
|
if (success) {
|
|
|
|
spdk_bdev_free_io(bdev_io);
|
|
|
|
bdevperf_verify_submit_read(cb_arg);
|
|
|
|
} else {
|
|
|
|
bdevperf_complete(bdev_io, success, cb_arg);
|
|
|
|
}
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
|
2018-06-22 14:22:22 -07:00
|
|
|
static void
|
|
|
|
bdevperf_zcopy_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
|
|
|
|
{
|
|
|
|
if (!success) {
|
|
|
|
bdevperf_complete(bdev_io, success, cb_arg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
spdk_bdev_zcopy_end(bdev_io, false, bdevperf_complete, cb_arg);
|
|
|
|
}
|
|
|
|
|
2016-07-20 11:16:23 -07:00
|
|
|
static __thread unsigned int seed = 0;
|
|
|
|
|
|
|
|
static void
|
2018-06-14 01:59:35 -07:00
|
|
|
bdevperf_prep_task(struct bdevperf_task *task)
|
2016-07-20 11:16:23 -07:00
|
|
|
{
|
2018-06-14 01:59:35 -07:00
|
|
|
struct io_target *target = task->target;
|
|
|
|
uint64_t offset_in_ios;
|
2016-07-20 11:16:23 -07:00
|
|
|
|
|
|
|
if (g_is_random) {
|
|
|
|
offset_in_ios = rand_r(&seed) % target->size_in_ios;
|
|
|
|
} else {
|
|
|
|
offset_in_ios = target->offset_in_ios++;
|
|
|
|
if (target->offset_in_ios == target->size_in_ios) {
|
|
|
|
target->offset_in_ios = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-08 09:12:27 -07:00
|
|
|
task->offset_blocks = offset_in_ios * target->io_size_blocks;
|
2018-05-31 06:16:10 -07:00
|
|
|
if (g_verify || g_reset) {
|
2019-02-11 08:28:02 +09:00
|
|
|
generate_data(task->buf, g_buf_size,
|
|
|
|
spdk_bdev_get_block_size(target->bdev),
|
|
|
|
spdk_bdev_get_md_size(target->bdev),
|
|
|
|
target->io_size_blocks, rand_r(&seed) % 256);
|
2016-07-20 11:16:23 -07:00
|
|
|
task->iov.iov_base = task->buf;
|
2019-02-11 08:28:02 +09:00
|
|
|
task->iov.iov_len = g_buf_size;
|
2018-06-14 02:03:52 -07:00
|
|
|
task->io_type = SPDK_BDEV_IO_TYPE_WRITE;
|
2018-06-07 09:29:50 +08:00
|
|
|
} else if (g_flush) {
|
2018-06-14 02:03:52 -07:00
|
|
|
task->io_type = SPDK_BDEV_IO_TYPE_FLUSH;
|
2018-05-31 06:16:10 -07:00
|
|
|
} else if (g_unmap) {
|
2018-06-14 02:03:52 -07:00
|
|
|
task->io_type = SPDK_BDEV_IO_TYPE_UNMAP;
|
2018-08-09 03:11:09 -04:00
|
|
|
} else if (g_write_zeroes) {
|
|
|
|
task->io_type = SPDK_BDEV_IO_TYPE_WRITE_ZEROES;
|
2016-07-20 11:16:23 -07:00
|
|
|
} else if ((g_rw_percentage == 100) ||
|
|
|
|
(g_rw_percentage != 0 && ((rand_r(&seed) % 100) < g_rw_percentage))) {
|
2018-06-14 02:03:52 -07:00
|
|
|
task->io_type = SPDK_BDEV_IO_TYPE_READ;
|
2016-07-20 11:16:23 -07:00
|
|
|
} else {
|
|
|
|
task->iov.iov_base = task->buf;
|
2019-02-11 08:28:02 +09:00
|
|
|
task->iov.iov_len = g_buf_size;
|
2018-06-14 02:03:52 -07:00
|
|
|
task->io_type = SPDK_BDEV_IO_TYPE_WRITE;
|
|
|
|
}
|
2018-06-14 01:59:35 -07:00
|
|
|
}
|
|
|
|
|
2019-02-11 08:40:32 +09:00
|
|
|
static int
|
|
|
|
bdevperf_generate_dif(struct bdevperf_task *task)
|
|
|
|
{
|
|
|
|
struct io_target *target = task->target;
|
|
|
|
struct spdk_bdev *bdev = target->bdev;
|
|
|
|
struct spdk_dif_ctx dif_ctx;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = spdk_dif_ctx_init(&dif_ctx,
|
|
|
|
spdk_bdev_get_block_size(bdev),
|
|
|
|
spdk_bdev_get_md_size(bdev),
|
|
|
|
spdk_bdev_is_md_interleaved(bdev),
|
|
|
|
spdk_bdev_is_dif_head_of_md(bdev),
|
|
|
|
spdk_bdev_get_dif_type(bdev),
|
|
|
|
target->dif_check_flags,
|
2019-02-13 15:17:49 +09:00
|
|
|
task->offset_blocks, 0, 0, 0);
|
2019-02-11 08:40:32 +09:00
|
|
|
if (rc != 0) {
|
|
|
|
fprintf(stderr, "Initialization of DIF context failed\n");
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = spdk_dif_generate(&task->iov, 1, target->io_size_blocks, &dif_ctx);
|
|
|
|
if (rc != 0) {
|
|
|
|
fprintf(stderr, "Generation of DIF failed\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2018-06-14 01:59:35 -07:00
|
|
|
static void
|
2018-06-12 08:11:31 -07:00
|
|
|
bdevperf_submit_task(void *arg)
|
2018-06-14 01:59:35 -07:00
|
|
|
{
|
2018-06-12 08:11:31 -07:00
|
|
|
struct bdevperf_task *task = arg;
|
2018-06-14 02:26:33 -07:00
|
|
|
struct io_target *target = task->target;
|
2018-06-14 01:59:35 -07:00
|
|
|
struct spdk_bdev_desc *desc;
|
|
|
|
struct spdk_io_channel *ch;
|
2018-06-14 02:26:33 -07:00
|
|
|
spdk_bdev_io_completion_cb cb_fn;
|
2019-02-11 08:40:32 +09:00
|
|
|
int rc = 0;
|
2018-06-14 01:59:35 -07:00
|
|
|
|
|
|
|
desc = target->bdev_desc;
|
|
|
|
ch = target->ch;
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
switch (task->io_type) {
|
|
|
|
case SPDK_BDEV_IO_TYPE_WRITE:
|
2019-02-11 08:40:32 +09:00
|
|
|
if (spdk_bdev_get_md_size(target->bdev) != 0 && target->dif_check_flags != 0) {
|
|
|
|
rc = bdevperf_generate_dif(task);
|
|
|
|
}
|
|
|
|
if (rc == 0) {
|
|
|
|
cb_fn = (g_verify || g_reset) ? bdevperf_verify_write_complete : bdevperf_complete;
|
|
|
|
rc = spdk_bdev_writev_blocks(desc, ch, &task->iov, 1, task->offset_blocks,
|
|
|
|
target->io_size_blocks, cb_fn, task);
|
|
|
|
}
|
2018-06-14 02:03:52 -07:00
|
|
|
break;
|
|
|
|
case SPDK_BDEV_IO_TYPE_FLUSH:
|
|
|
|
rc = spdk_bdev_flush_blocks(desc, ch, task->offset_blocks,
|
|
|
|
target->io_size_blocks, bdevperf_complete, task);
|
|
|
|
break;
|
|
|
|
case SPDK_BDEV_IO_TYPE_UNMAP:
|
|
|
|
rc = spdk_bdev_unmap_blocks(desc, ch, task->offset_blocks,
|
|
|
|
target->io_size_blocks, bdevperf_complete, task);
|
|
|
|
break;
|
2018-08-09 03:11:09 -04:00
|
|
|
case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
|
|
|
|
rc = spdk_bdev_write_zeroes_blocks(desc, ch, task->offset_blocks,
|
|
|
|
target->io_size_blocks, bdevperf_complete, task);
|
|
|
|
break;
|
2018-06-14 02:03:52 -07:00
|
|
|
case SPDK_BDEV_IO_TYPE_READ:
|
2018-06-22 14:22:22 -07:00
|
|
|
if (g_zcopy) {
|
|
|
|
rc = spdk_bdev_zcopy_start(desc, ch, task->offset_blocks, target->io_size_blocks,
|
|
|
|
true, bdevperf_zcopy_complete, task);
|
|
|
|
} else {
|
|
|
|
rc = spdk_bdev_read_blocks(desc, ch, task->buf, task->offset_blocks,
|
|
|
|
target->io_size_blocks, bdevperf_complete, task);
|
|
|
|
}
|
2018-06-14 02:03:52 -07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
rc = -EINVAL;
|
|
|
|
break;
|
2018-06-13 07:04:46 -07:00
|
|
|
}
|
|
|
|
|
2018-06-12 08:11:31 -07:00
|
|
|
if (rc == -ENOMEM) {
|
|
|
|
task->bdev_io_wait.bdev = target->bdev;
|
|
|
|
task->bdev_io_wait.cb_fn = bdevperf_submit_task;
|
|
|
|
task->bdev_io_wait.cb_arg = task;
|
|
|
|
spdk_bdev_queue_io_wait(target->bdev, ch, &task->bdev_io_wait);
|
|
|
|
return;
|
|
|
|
} else if (rc != 0) {
|
2018-06-13 07:04:46 -07:00
|
|
|
printf("Failed to submit bdev_io: %d\n", rc);
|
|
|
|
target->is_draining = true;
|
|
|
|
g_run_failed = true;
|
|
|
|
return;
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
target->current_queue_depth++;
|
|
|
|
}
|
|
|
|
|
2018-06-14 02:26:33 -07:00
|
|
|
static void
|
|
|
|
bdevperf_submit_single(struct io_target *target, struct bdevperf_task *task)
|
|
|
|
{
|
|
|
|
if (!task) {
|
|
|
|
if (!TAILQ_EMPTY(&target->task_list)) {
|
|
|
|
task = TAILQ_FIRST(&target->task_list);
|
|
|
|
TAILQ_REMOVE(&target->task_list, task, link);
|
|
|
|
} else {
|
|
|
|
printf("Task allocation failed\n");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bdevperf_prep_task(task);
|
|
|
|
bdevperf_submit_task(task);
|
|
|
|
}
|
|
|
|
|
2016-07-20 11:16:23 -07:00
|
|
|
static void
|
|
|
|
bdevperf_submit_io(struct io_target *target, int queue_depth)
|
|
|
|
{
|
|
|
|
while (queue_depth-- > 0) {
|
2018-01-05 15:10:18 -07:00
|
|
|
bdevperf_submit_single(target, NULL);
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-12 17:16:47 -07:00
|
|
|
static int
|
2016-10-05 10:37:11 -07:00
|
|
|
end_target(void *arg)
|
2016-07-20 11:16:23 -07:00
|
|
|
{
|
|
|
|
struct io_target *target = arg;
|
|
|
|
|
2017-11-15 15:09:40 -07:00
|
|
|
spdk_poller_unregister(&target->run_timer);
|
2016-07-20 11:16:23 -07:00
|
|
|
if (g_reset) {
|
2017-11-15 15:09:40 -07:00
|
|
|
spdk_poller_unregister(&target->reset_timer);
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
target->is_draining = true;
|
2018-03-12 17:16:47 -07:00
|
|
|
|
|
|
|
return -1;
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
|
2018-03-12 17:16:47 -07:00
|
|
|
static int reset_target(void *arg);
|
2016-07-20 11:16:23 -07:00
|
|
|
|
|
|
|
static void
|
2017-05-16 13:25:03 -07:00
|
|
|
reset_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
|
2016-07-20 11:16:23 -07:00
|
|
|
{
|
2016-10-05 15:00:20 -07:00
|
|
|
struct bdevperf_task *task = cb_arg;
|
2016-07-20 11:16:23 -07:00
|
|
|
struct io_target *target = task->target;
|
|
|
|
|
2017-05-16 13:25:03 -07:00
|
|
|
if (!success) {
|
2017-05-10 13:29:31 -07:00
|
|
|
printf("Reset blockdev=%s failed\n", spdk_bdev_get_name(target->bdev));
|
2016-10-11 08:34:53 -07:00
|
|
|
target->is_draining = true;
|
2016-07-20 11:16:23 -07:00
|
|
|
g_run_failed = true;
|
|
|
|
}
|
|
|
|
|
2017-11-20 18:22:45 +08:00
|
|
|
TAILQ_INSERT_TAIL(&target->task_list, task, link);
|
2017-06-05 11:39:38 -07:00
|
|
|
spdk_bdev_free_io(bdev_io);
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2017-11-14 13:42:17 -07:00
|
|
|
target->reset_timer = spdk_poller_register(reset_target, target,
|
|
|
|
10 * 1000000);
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
|
2018-03-12 17:16:47 -07:00
|
|
|
static int
|
2016-10-05 10:37:11 -07:00
|
|
|
reset_target(void *arg)
|
2016-07-20 11:16:23 -07:00
|
|
|
{
|
|
|
|
struct io_target *target = arg;
|
|
|
|
struct bdevperf_task *task = NULL;
|
2017-06-05 11:39:38 -07:00
|
|
|
int rc;
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2017-11-15 15:09:40 -07:00
|
|
|
spdk_poller_unregister(&target->reset_timer);
|
2016-10-05 10:37:11 -07:00
|
|
|
|
2016-07-20 11:16:23 -07:00
|
|
|
/* Do reset. */
|
2017-11-20 18:22:45 +08:00
|
|
|
task = TAILQ_FIRST(&target->task_list);
|
|
|
|
if (!task) {
|
|
|
|
printf("Task allocation failed\n");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
TAILQ_REMOVE(&target->task_list, task, link);
|
|
|
|
|
2017-07-06 12:39:19 -07:00
|
|
|
rc = spdk_bdev_reset(target->bdev_desc, target->ch,
|
2017-06-05 11:39:38 -07:00
|
|
|
reset_cb, task);
|
|
|
|
if (rc) {
|
|
|
|
printf("Reset failed: %d\n", rc);
|
|
|
|
target->is_draining = true;
|
|
|
|
g_run_failed = true;
|
|
|
|
}
|
2018-03-12 17:16:47 -07:00
|
|
|
|
|
|
|
return -1;
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-01-04 18:19:02 -07:00
|
|
|
bdevperf_submit_on_core(void *arg1, void *arg2)
|
2016-07-20 11:16:23 -07:00
|
|
|
{
|
2017-01-04 18:19:02 -07:00
|
|
|
struct io_target *target = arg1;
|
2016-07-20 11:16:23 -07:00
|
|
|
|
|
|
|
/* Submit initial I/O for each block device. Each time one
|
|
|
|
* completes, another will be submitted. */
|
|
|
|
while (target != NULL) {
|
2017-06-29 11:23:50 -07:00
|
|
|
target->ch = spdk_bdev_get_io_channel(target->bdev_desc);
|
2017-11-07 01:30:09 -05:00
|
|
|
if (!target->ch) {
|
|
|
|
printf("Skip this device (%s) as IO channel not setup.\n",
|
|
|
|
spdk_bdev_get_name(target->bdev));
|
|
|
|
g_target_count--;
|
|
|
|
g_run_failed = true;
|
|
|
|
spdk_bdev_close(target->bdev_desc);
|
|
|
|
|
|
|
|
target = target->next;
|
|
|
|
continue;
|
|
|
|
}
|
2016-09-20 16:18:44 -07:00
|
|
|
|
2016-07-20 11:16:23 -07:00
|
|
|
/* Start a timer to stop this I/O chain when the run is over */
|
2017-11-14 13:42:17 -07:00
|
|
|
target->run_timer = spdk_poller_register(end_target, target,
|
2017-12-07 17:21:31 +08:00
|
|
|
g_time_in_usec);
|
2016-07-20 11:16:23 -07:00
|
|
|
if (g_reset) {
|
2017-11-14 13:42:17 -07:00
|
|
|
target->reset_timer = spdk_poller_register(reset_target, target,
|
|
|
|
10 * 1000000);
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
bdevperf_submit_io(target, g_queue_depth);
|
|
|
|
target = target->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-09 13:54:39 +02:00
|
|
|
static void
|
|
|
|
bdevperf_usage(void)
|
2016-07-20 11:16:23 -07:00
|
|
|
{
|
2018-08-09 13:54:39 +02:00
|
|
|
printf(" -q <depth> io depth\n");
|
|
|
|
printf(" -o <size> io size in bytes\n");
|
|
|
|
printf(" -w <type> io pattern type, must be one of (read, write, randread, randwrite, rw, randrw, verify, reset, unmap, flush)\n");
|
|
|
|
printf(" -t <time> time in seconds\n");
|
|
|
|
printf(" -M <percent> rwmixread (100 for reads, 0 for writes)\n");
|
|
|
|
printf(" -P <num> number of moving average period\n");
|
2018-03-06 09:13:05 +09:00
|
|
|
printf("\t\t(If set to n, show weighted mean of the previous n IO/s in real time)\n");
|
|
|
|
printf("\t\t(Formula: M = 2 / (n + 1), EMA[i+1] = IO/s * M + (1 - M) * EMA[i])\n");
|
|
|
|
printf("\t\t(only valid with -S)\n");
|
2019-02-04 10:44:34 -07:00
|
|
|
printf(" -S <period> show performance result in real time every <period> seconds\n");
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
|
2018-03-06 09:13:05 +09:00
|
|
|
/*
|
|
|
|
* Cumulative Moving Average (CMA): average of all data up to current
|
|
|
|
* Exponential Moving Average (EMA): weighted mean of the previous n data and more weight is given to recent
|
|
|
|
* Simple Moving Average (SMA): unweighted mean of the previous n data
|
|
|
|
*
|
|
|
|
* Bdevperf supports CMA and EMA.
|
|
|
|
*/
|
|
|
|
static double
|
|
|
|
get_cma_io_per_second(struct io_target *target, uint64_t io_time_in_usec)
|
|
|
|
{
|
|
|
|
return (double)target->io_completed * 1000000 / io_time_in_usec;
|
|
|
|
}
|
|
|
|
|
|
|
|
static double
|
|
|
|
get_ema_io_per_second(struct io_target *target, uint64_t ema_period)
|
|
|
|
{
|
|
|
|
double io_completed, io_per_second;
|
|
|
|
|
|
|
|
io_completed = target->io_completed;
|
|
|
|
io_per_second = (double)(io_completed - target->prev_io_completed) * 1000000
|
|
|
|
/ g_show_performance_period_in_usec;
|
|
|
|
target->prev_io_completed = io_completed;
|
|
|
|
|
|
|
|
target->ema_io_per_second += (io_per_second - target->ema_io_per_second) * 2
|
|
|
|
/ (ema_period + 1);
|
|
|
|
return target->ema_io_per_second;
|
|
|
|
}
|
|
|
|
|
2016-07-20 11:16:23 -07:00
|
|
|
static void
|
2018-03-06 09:13:05 +09:00
|
|
|
performance_dump(uint64_t io_time_in_usec, uint64_t ema_period)
|
2016-07-20 11:16:23 -07:00
|
|
|
{
|
2017-03-27 12:59:40 -07:00
|
|
|
uint32_t index;
|
2016-07-20 11:16:23 -07:00
|
|
|
unsigned lcore_id;
|
2017-12-19 12:28:30 -07:00
|
|
|
double io_per_second, mb_per_second;
|
|
|
|
double total_io_per_second, total_mb_per_second;
|
2016-07-20 11:16:23 -07:00
|
|
|
struct io_target *target;
|
|
|
|
|
|
|
|
total_io_per_second = 0;
|
|
|
|
total_mb_per_second = 0;
|
2017-03-27 12:59:40 -07:00
|
|
|
for (index = 0; index < spdk_env_get_core_count(); index++) {
|
2018-07-11 21:13:39 -07:00
|
|
|
target = g_head[index];
|
2016-07-20 11:16:23 -07:00
|
|
|
if (target != NULL) {
|
|
|
|
lcore_id = target->lcore;
|
2016-09-29 04:55:56 +08:00
|
|
|
printf("\r Logical core: %u\n", lcore_id);
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
while (target != NULL) {
|
2018-03-06 09:13:05 +09:00
|
|
|
if (ema_period == 0) {
|
|
|
|
io_per_second = get_cma_io_per_second(target, io_time_in_usec);
|
|
|
|
} else {
|
|
|
|
io_per_second = get_ema_io_per_second(target, ema_period);
|
|
|
|
}
|
|
|
|
mb_per_second = io_per_second * g_io_size / (1024 * 1024);
|
2016-07-20 11:16:23 -07:00
|
|
|
printf("\r %-20s: %10.2f IO/s %10.2f MB/s\n",
|
2017-11-02 12:40:01 +08:00
|
|
|
target->name, io_per_second, mb_per_second);
|
2016-07-20 11:16:23 -07:00
|
|
|
total_io_per_second += io_per_second;
|
|
|
|
total_mb_per_second += mb_per_second;
|
|
|
|
target = target->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("\r =====================================================\n");
|
|
|
|
printf("\r %-20s: %10.2f IO/s %10.2f MB/s\n",
|
|
|
|
"Total", total_io_per_second, total_mb_per_second);
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-03-12 17:16:47 -07:00
|
|
|
static int
|
2016-10-05 10:37:11 -07:00
|
|
|
performance_statistics_thread(void *arg)
|
2016-07-20 11:16:23 -07:00
|
|
|
{
|
2017-12-13 05:27:17 +08:00
|
|
|
g_show_performance_period_num++;
|
2018-03-06 09:13:05 +09:00
|
|
|
performance_dump(g_show_performance_period_num * g_show_performance_period_in_usec,
|
|
|
|
g_show_performance_ema_period);
|
2018-03-12 17:16:47 -07:00
|
|
|
return -1;
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
|
2019-02-11 08:02:48 +09:00
|
|
|
static struct bdevperf_task *bdevperf_construct_task_on_target(struct io_target *target)
|
|
|
|
{
|
|
|
|
struct bdevperf_task *task;
|
|
|
|
|
|
|
|
task = calloc(1, sizeof(struct bdevperf_task));
|
|
|
|
if (!task) {
|
|
|
|
fprintf(stderr, "Failed to allocate task from memory\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
task->buf = spdk_dma_zmalloc(g_io_size, g_min_alignment, NULL);
|
|
|
|
if (!task->buf) {
|
|
|
|
fprintf(stderr, "Cannot allocate buf for task=%p\n", task);
|
|
|
|
free(task);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
task->target = target;
|
|
|
|
|
|
|
|
return task;
|
|
|
|
}
|
|
|
|
|
2017-11-20 18:22:45 +08:00
|
|
|
static int
|
|
|
|
bdevperf_construct_targets_tasks(void)
|
2016-07-20 11:16:23 -07:00
|
|
|
{
|
2017-04-03 15:02:49 -07:00
|
|
|
uint32_t i;
|
2016-07-20 11:16:23 -07:00
|
|
|
struct io_target *target;
|
2017-11-20 18:22:45 +08:00
|
|
|
struct bdevperf_task *task;
|
|
|
|
int j, task_num = g_queue_depth;
|
2017-06-27 17:20:45 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create the task pool after we have enumerated the targets, so that we know
|
|
|
|
* the min buffer alignment. Some backends such as AIO have alignment restrictions
|
|
|
|
* that must be accounted for.
|
|
|
|
*/
|
2017-11-20 18:22:45 +08:00
|
|
|
if (g_reset) {
|
|
|
|
task_num += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize task list for each target */
|
|
|
|
for (i = 0; i < spdk_env_get_core_count(); i++) {
|
2018-07-11 21:13:39 -07:00
|
|
|
target = g_head[i];
|
2017-11-20 18:22:45 +08:00
|
|
|
if (!target) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
while (target != NULL) {
|
|
|
|
for (j = 0; j < task_num; j++) {
|
2019-02-11 08:02:48 +09:00
|
|
|
task = bdevperf_construct_task_on_target(target);
|
|
|
|
if (task == NULL) {
|
2017-11-20 18:22:45 +08:00
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
TAILQ_INSERT_TAIL(&target->task_list, task, link);
|
|
|
|
}
|
|
|
|
target = target->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ret:
|
|
|
|
fprintf(stderr, "Bdevperf program exits due to memory allocation issue\n");
|
|
|
|
fprintf(stderr, "Use -d XXX to allocate more huge pages, e.g., -d 4096\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-03-04 13:52:59 -07:00
|
|
|
bdevperf_run(void *arg1)
|
2017-11-20 18:22:45 +08:00
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
struct io_target *target;
|
|
|
|
struct spdk_event *event;
|
|
|
|
int rc;
|
|
|
|
|
2017-12-26 19:48:59 +08:00
|
|
|
rc = blockdev_heads_init();
|
|
|
|
if (rc) {
|
|
|
|
spdk_app_stop(1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-20 18:22:45 +08:00
|
|
|
bdevperf_construct_targets();
|
|
|
|
|
2018-06-06 03:54:00 -07:00
|
|
|
if (g_target_count == 0) {
|
|
|
|
fprintf(stderr, "No valid bdevs found.\n");
|
|
|
|
spdk_app_stop(1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-20 18:22:45 +08:00
|
|
|
rc = bdevperf_construct_targets_tasks();
|
|
|
|
if (rc) {
|
2017-11-13 12:09:32 +08:00
|
|
|
spdk_app_stop(1);
|
|
|
|
return;
|
|
|
|
}
|
2017-06-06 14:31:47 +08:00
|
|
|
|
2017-12-07 17:21:31 +08:00
|
|
|
printf("Running I/O for %" PRIu64 " seconds...\n", g_time_in_usec / 1000000);
|
2016-07-20 11:16:23 -07:00
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
/* Start a timer to dump performance numbers */
|
2017-12-07 17:21:31 +08:00
|
|
|
g_shutdown_tsc = spdk_get_ticks();
|
2016-07-20 11:16:23 -07:00
|
|
|
if (g_show_performance_real_time) {
|
2017-12-13 05:27:17 +08:00
|
|
|
g_perf_timer = spdk_poller_register(performance_statistics_thread, NULL,
|
|
|
|
g_show_performance_period_in_usec);
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
|
2017-11-20 18:22:45 +08:00
|
|
|
g_master_core = spdk_env_get_current_core();
|
2016-07-20 11:16:23 -07:00
|
|
|
/* Send events to start all I/O */
|
2017-08-16 14:37:24 -07:00
|
|
|
for (i = 0; i < spdk_env_get_core_count(); i++) {
|
2018-07-11 21:13:39 -07:00
|
|
|
target = g_head[i];
|
2017-08-16 14:37:24 -07:00
|
|
|
if (target == NULL) {
|
|
|
|
break;
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
2017-08-16 14:37:24 -07:00
|
|
|
event = spdk_event_allocate(target->lcore, bdevperf_submit_on_core,
|
|
|
|
target, NULL);
|
|
|
|
spdk_event_call(event);
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-07 17:21:31 +08:00
|
|
|
static void
|
|
|
|
bdevperf_stop_io_on_core(void *arg1, void *arg2)
|
|
|
|
{
|
|
|
|
struct io_target *target = arg1;
|
|
|
|
|
|
|
|
/* Stop I/O for each block device. */
|
|
|
|
while (target != NULL) {
|
|
|
|
end_target(target);
|
|
|
|
target = target->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
spdk_bdevperf_shutdown_cb(void)
|
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
struct io_target *target;
|
|
|
|
struct spdk_event *event;
|
|
|
|
|
|
|
|
g_shutdown = true;
|
|
|
|
g_shutdown_tsc = spdk_get_ticks() - g_shutdown_tsc;
|
|
|
|
|
|
|
|
/* Send events to stop all I/O on each core */
|
|
|
|
for (i = 0; i < spdk_env_get_core_count(); i++) {
|
2018-07-16 17:03:12 -07:00
|
|
|
if (g_head == NULL) {
|
|
|
|
break;
|
|
|
|
}
|
2018-07-11 21:13:39 -07:00
|
|
|
target = g_head[i];
|
2017-12-07 17:21:31 +08:00
|
|
|
if (target == NULL) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
event = spdk_event_allocate(target->lcore, bdevperf_stop_io_on_core,
|
|
|
|
target, NULL);
|
|
|
|
spdk_event_call(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-08 14:40:06 -05:00
|
|
|
static int
|
2018-08-09 13:54:39 +02:00
|
|
|
bdevperf_parse_arg(int ch, char *arg)
|
|
|
|
{
|
2019-01-17 09:05:53 -05:00
|
|
|
long long tmp;
|
|
|
|
|
|
|
|
if (ch == 'w') {
|
2018-08-09 13:54:39 +02:00
|
|
|
g_workload_type = optarg;
|
2019-01-17 09:05:53 -05:00
|
|
|
} else {
|
2019-01-23 08:27:37 +09:00
|
|
|
tmp = spdk_strtoll(optarg, 10);
|
|
|
|
if (tmp < 0) {
|
|
|
|
fprintf(stderr, "Parse failed for the option %c.\n", ch);
|
|
|
|
return tmp;
|
|
|
|
} else if (tmp >= INT_MAX) {
|
|
|
|
fprintf(stderr, "Parsed option was too large %c.\n", ch);
|
2019-01-17 09:05:53 -05:00
|
|
|
return -ERANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (ch) {
|
|
|
|
case 'q':
|
|
|
|
g_queue_depth = tmp;
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
g_io_size = tmp;
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
g_time_in_sec = tmp;
|
|
|
|
break;
|
|
|
|
case 'M':
|
|
|
|
g_rw_percentage = tmp;
|
|
|
|
g_mix_specified = true;
|
|
|
|
break;
|
|
|
|
case 'P':
|
|
|
|
g_show_performance_ema_period = tmp;
|
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
g_show_performance_real_time = 1;
|
|
|
|
g_show_performance_period_in_usec = tmp * 1000000;
|
|
|
|
g_show_performance_period_in_usec = spdk_max(g_show_performance_period_in_usec,
|
|
|
|
g_show_performance_period_in_usec);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2018-08-09 13:54:39 +02:00
|
|
|
}
|
2019-01-08 14:40:06 -05:00
|
|
|
return 0;
|
2018-08-09 13:54:39 +02:00
|
|
|
}
|
|
|
|
|
2016-07-20 11:16:23 -07:00
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
2017-06-06 14:31:47 +08:00
|
|
|
struct spdk_app_opts opts = {};
|
2018-02-22 18:50:51 -05:00
|
|
|
int rc;
|
2018-08-09 13:54:39 +02:00
|
|
|
|
|
|
|
spdk_app_opts_init(&opts);
|
2018-10-18 18:27:58 -04:00
|
|
|
opts.name = "bdevperf";
|
2018-08-09 13:54:39 +02:00
|
|
|
opts.rpc_addr = NULL;
|
|
|
|
opts.reactor_mask = NULL;
|
|
|
|
opts.shutdown_cb = spdk_bdevperf_shutdown_cb;
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2017-04-24 11:14:41 -07:00
|
|
|
/* default value */
|
2016-07-20 11:16:23 -07:00
|
|
|
g_queue_depth = 0;
|
|
|
|
g_io_size = 0;
|
2018-08-09 13:54:39 +02:00
|
|
|
g_workload_type = NULL;
|
|
|
|
g_time_in_sec = 0;
|
|
|
|
g_mix_specified = false;
|
2018-06-14 11:57:01 +08:00
|
|
|
|
2018-08-09 13:54:39 +02:00
|
|
|
if ((rc = spdk_app_parse_args(argc, argv, &opts, "q:o:t:w:M:P:S:", NULL,
|
|
|
|
bdevperf_parse_arg, bdevperf_usage)) !=
|
|
|
|
SPDK_APP_PARSE_ARGS_SUCCESS) {
|
|
|
|
return rc;
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
|
2017-03-28 10:51:42 -07:00
|
|
|
if (g_queue_depth <= 0) {
|
2018-08-09 13:54:39 +02:00
|
|
|
spdk_app_usage();
|
2018-08-31 13:43:13 -07:00
|
|
|
bdevperf_usage();
|
2016-07-20 11:16:23 -07:00
|
|
|
exit(1);
|
|
|
|
}
|
2017-03-28 10:51:42 -07:00
|
|
|
if (g_io_size <= 0) {
|
2018-08-09 13:54:39 +02:00
|
|
|
spdk_app_usage();
|
2018-08-31 13:43:13 -07:00
|
|
|
bdevperf_usage();
|
2016-07-20 11:16:23 -07:00
|
|
|
exit(1);
|
|
|
|
}
|
2018-08-09 13:54:39 +02:00
|
|
|
if (!g_workload_type) {
|
|
|
|
spdk_app_usage();
|
2018-08-31 13:43:13 -07:00
|
|
|
bdevperf_usage();
|
2016-07-20 11:16:23 -07:00
|
|
|
exit(1);
|
|
|
|
}
|
2018-08-09 13:54:39 +02:00
|
|
|
if (g_time_in_sec <= 0) {
|
|
|
|
spdk_app_usage();
|
2018-08-31 13:43:13 -07:00
|
|
|
bdevperf_usage();
|
2016-07-20 11:16:23 -07:00
|
|
|
exit(1);
|
|
|
|
}
|
2018-08-09 13:54:39 +02:00
|
|
|
g_time_in_usec = g_time_in_sec * 1000000LL;
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2018-03-06 09:13:05 +09:00
|
|
|
if (g_show_performance_ema_period > 0 &&
|
|
|
|
g_show_performance_real_time == 0) {
|
|
|
|
fprintf(stderr, "-P option must be specified with -S option\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2018-08-09 13:54:39 +02:00
|
|
|
if (strcmp(g_workload_type, "read") &&
|
|
|
|
strcmp(g_workload_type, "write") &&
|
|
|
|
strcmp(g_workload_type, "randread") &&
|
|
|
|
strcmp(g_workload_type, "randwrite") &&
|
|
|
|
strcmp(g_workload_type, "rw") &&
|
|
|
|
strcmp(g_workload_type, "randrw") &&
|
|
|
|
strcmp(g_workload_type, "verify") &&
|
|
|
|
strcmp(g_workload_type, "reset") &&
|
|
|
|
strcmp(g_workload_type, "unmap") &&
|
2018-08-09 03:11:09 -04:00
|
|
|
strcmp(g_workload_type, "write_zeroes") &&
|
2018-08-09 13:54:39 +02:00
|
|
|
strcmp(g_workload_type, "flush")) {
|
2016-07-20 11:16:23 -07:00
|
|
|
fprintf(stderr,
|
|
|
|
"io pattern type must be one of\n"
|
2018-06-07 09:29:50 +08:00
|
|
|
"(read, write, randread, randwrite, rw, randrw, verify, reset, unmap, flush)\n");
|
2016-07-20 11:16:23 -07:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2018-08-09 13:54:39 +02:00
|
|
|
if (!strcmp(g_workload_type, "read") ||
|
|
|
|
!strcmp(g_workload_type, "randread")) {
|
2016-07-20 11:16:23 -07:00
|
|
|
g_rw_percentage = 100;
|
|
|
|
}
|
|
|
|
|
2018-08-09 13:54:39 +02:00
|
|
|
if (!strcmp(g_workload_type, "write") ||
|
|
|
|
!strcmp(g_workload_type, "randwrite")) {
|
2016-07-20 11:16:23 -07:00
|
|
|
g_rw_percentage = 0;
|
|
|
|
}
|
|
|
|
|
2018-08-09 13:54:39 +02:00
|
|
|
if (!strcmp(g_workload_type, "unmap")) {
|
2018-05-31 06:16:10 -07:00
|
|
|
g_unmap = true;
|
|
|
|
}
|
|
|
|
|
2018-08-09 03:11:09 -04:00
|
|
|
if (!strcmp(g_workload_type, "write_zeroes")) {
|
|
|
|
g_write_zeroes = true;
|
|
|
|
}
|
|
|
|
|
2018-08-09 13:54:39 +02:00
|
|
|
if (!strcmp(g_workload_type, "flush")) {
|
2018-06-07 09:29:50 +08:00
|
|
|
g_flush = true;
|
|
|
|
}
|
|
|
|
|
2018-08-09 13:54:39 +02:00
|
|
|
if (!strcmp(g_workload_type, "verify") ||
|
|
|
|
!strcmp(g_workload_type, "reset")) {
|
2016-07-20 11:16:23 -07:00
|
|
|
g_rw_percentage = 50;
|
2017-05-05 13:15:51 -07:00
|
|
|
if (g_io_size > SPDK_BDEV_LARGE_BUF_MAX_SIZE) {
|
2016-07-20 11:16:23 -07:00
|
|
|
fprintf(stderr, "Unable to exceed max I/O size of %d for verify. (%d provided).\n",
|
2017-05-05 13:15:51 -07:00
|
|
|
SPDK_BDEV_LARGE_BUF_MAX_SIZE, g_io_size);
|
2016-07-20 11:16:23 -07:00
|
|
|
exit(1);
|
|
|
|
}
|
2018-08-09 13:54:39 +02:00
|
|
|
if (opts.reactor_mask) {
|
2016-07-20 11:16:23 -07:00
|
|
|
fprintf(stderr, "Ignoring -m option. Verify can only run with a single core.\n");
|
2018-08-09 13:54:39 +02:00
|
|
|
opts.reactor_mask = NULL;
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|
|
|
|
g_verify = true;
|
2018-08-09 13:54:39 +02:00
|
|
|
if (!strcmp(g_workload_type, "reset")) {
|
2016-07-20 11:16:23 -07:00
|
|
|
g_reset = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-09 13:54:39 +02:00
|
|
|
if (!strcmp(g_workload_type, "read") ||
|
|
|
|
!strcmp(g_workload_type, "randread") ||
|
|
|
|
!strcmp(g_workload_type, "write") ||
|
|
|
|
!strcmp(g_workload_type, "randwrite") ||
|
|
|
|
!strcmp(g_workload_type, "verify") ||
|
|
|
|
!strcmp(g_workload_type, "reset") ||
|
|
|
|
!strcmp(g_workload_type, "unmap") ||
|
2018-08-09 03:11:09 -04:00
|
|
|
!strcmp(g_workload_type, "write_zeroes") ||
|
2018-08-09 13:54:39 +02:00
|
|
|
!strcmp(g_workload_type, "flush")) {
|
|
|
|
if (g_mix_specified) {
|
2016-07-20 11:16:23 -07:00
|
|
|
fprintf(stderr, "Ignoring -M option... Please use -M option"
|
|
|
|
" only when using rw or randrw.\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-09 13:54:39 +02:00
|
|
|
if (!strcmp(g_workload_type, "rw") ||
|
|
|
|
!strcmp(g_workload_type, "randrw")) {
|
2016-07-20 11:16:23 -07:00
|
|
|
if (g_rw_percentage < 0 || g_rw_percentage > 100) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"-M must be specified to value from 0 to 100 "
|
|
|
|
"for rw or randrw.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-09 13:54:39 +02:00
|
|
|
if (!strcmp(g_workload_type, "read") ||
|
|
|
|
!strcmp(g_workload_type, "write") ||
|
|
|
|
!strcmp(g_workload_type, "rw") ||
|
|
|
|
!strcmp(g_workload_type, "verify") ||
|
|
|
|
!strcmp(g_workload_type, "reset") ||
|
2018-08-09 03:11:09 -04:00
|
|
|
!strcmp(g_workload_type, "unmap") ||
|
|
|
|
!strcmp(g_workload_type, "write_zeroes")) {
|
2016-07-20 11:16:23 -07:00
|
|
|
g_is_random = 0;
|
|
|
|
} else {
|
|
|
|
g_is_random = 1;
|
|
|
|
}
|
|
|
|
|
2017-05-05 13:15:51 -07:00
|
|
|
if (g_io_size > SPDK_BDEV_LARGE_BUF_MAX_SIZE) {
|
2018-08-27 16:24:40 +08:00
|
|
|
printf("I/O size of %d is greater than zero copy threshold (%d).\n",
|
2017-12-07 17:21:31 +08:00
|
|
|
g_io_size, SPDK_BDEV_LARGE_BUF_MAX_SIZE);
|
|
|
|
printf("Zero copy mechanism will not be used.\n");
|
2016-07-20 11:16:23 -07:00
|
|
|
g_zcopy = false;
|
|
|
|
}
|
|
|
|
|
2019-02-28 14:42:07 -07:00
|
|
|
rc = spdk_app_start(&opts, bdevperf_run, NULL);
|
2018-02-22 18:50:51 -05:00
|
|
|
if (rc) {
|
|
|
|
g_run_failed = true;
|
|
|
|
}
|
2016-07-20 11:16:23 -07:00
|
|
|
|
2017-12-07 17:21:31 +08:00
|
|
|
if (g_shutdown) {
|
|
|
|
g_time_in_usec = g_shutdown_tsc * 1000000 / spdk_get_ticks_hz();
|
|
|
|
printf("Received shutdown signal, test time is about %.6f seconds\n",
|
|
|
|
(double)g_time_in_usec / 1000000);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_time_in_usec) {
|
2018-06-06 03:54:00 -07:00
|
|
|
if (!g_run_failed) {
|
|
|
|
performance_dump(g_time_in_usec, 0);
|
|
|
|
}
|
2017-12-07 17:21:31 +08:00
|
|
|
} else {
|
|
|
|
printf("Test time less than one microsecond, no performance data will be shown\n");
|
|
|
|
}
|
|
|
|
|
2017-11-02 12:40:01 +08:00
|
|
|
blockdev_heads_destroy();
|
2016-07-20 11:16:23 -07:00
|
|
|
spdk_app_fini();
|
2016-10-10 11:25:56 -07:00
|
|
|
return g_run_failed;
|
2016-07-20 11:16:23 -07:00
|
|
|
}
|