2022-06-03 19:15:11 +00:00
|
|
|
/* SPDX-License-Identifier: BSD-3-Clause
|
2022-11-01 20:26:26 +00:00
|
|
|
* Copyright (C) 2016 Intel Corporation.
|
2023-01-20 04:37:08 +00:00
|
|
|
* Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES.
|
2016-07-20 18:16:23 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
2017-05-02 18:18:25 +00:00
|
|
|
#include "spdk/stdinc.h"
|
2016-07-20 18:16:23 +00:00
|
|
|
|
|
|
|
#include "spdk/bdev.h"
|
2022-08-08 20:31:08 +00:00
|
|
|
#include "spdk/accel.h"
|
2016-08-22 22:23:38 +00:00
|
|
|
#include "spdk/endian.h"
|
2016-08-17 20:35:18 +00:00
|
|
|
#include "spdk/env.h"
|
2016-10-05 17:37:11 +00:00
|
|
|
#include "spdk/event.h"
|
2016-07-20 18:16:23 +00:00
|
|
|
#include "spdk/log.h"
|
2017-05-09 17:57:32 +00:00
|
|
|
#include "spdk/util.h"
|
2018-06-11 20:32:15 +00:00
|
|
|
#include "spdk/thread.h"
|
2017-11-02 04:40:01 +00:00
|
|
|
#include "spdk/string.h"
|
2019-07-03 13:56:14 +00:00
|
|
|
#include "spdk/rpc.h"
|
2020-03-06 14:57:22 +00:00
|
|
|
#include "spdk/bit_array.h"
|
2020-06-26 08:29:03 +00:00
|
|
|
#include "spdk/conf.h"
|
2021-05-06 23:12:43 +00:00
|
|
|
#include "spdk/zipf.h"
|
2022-09-13 10:07:09 +00:00
|
|
|
#include "spdk/histogram_data.h"
|
2020-06-26 08:29:03 +00:00
|
|
|
|
2020-07-21 16:23:28 +00:00
|
|
|
#define BDEVPERF_CONFIG_MAX_FILENAME 1024
|
2020-06-26 08:29:03 +00:00
|
|
|
#define BDEVPERF_CONFIG_UNDEFINED -1
|
|
|
|
#define BDEVPERF_CONFIG_ERROR -2
|
2016-07-20 18:16:23 +00:00
|
|
|
|
|
|
|
struct bdevperf_task {
|
2017-01-18 18:28:17 +00:00
|
|
|
struct iovec iov;
|
2020-03-18 22:21:37 +00:00
|
|
|
struct bdevperf_job *job;
|
2019-09-10 02:44:11 +00:00
|
|
|
struct spdk_bdev_io *bdev_io;
|
2017-01-18 18:28:17 +00:00
|
|
|
void *buf;
|
2019-04-16 08:17:59 +00:00
|
|
|
void *md_buf;
|
2017-09-08 16:12:27 +00:00
|
|
|
uint64_t offset_blocks;
|
2020-05-26 22:05:37 +00:00
|
|
|
struct bdevperf_task *task_to_abort;
|
2018-06-14 09:03:52 +00:00
|
|
|
enum spdk_bdev_io_type io_type;
|
2017-11-20 10:22:45 +00:00
|
|
|
TAILQ_ENTRY(bdevperf_task) link;
|
2018-06-12 15:11:31 +00:00
|
|
|
struct spdk_bdev_io_wait_entry bdev_io_wait;
|
2016-07-20 18:16:23 +00:00
|
|
|
};
|
|
|
|
|
2020-02-05 08:02:44 +00:00
|
|
|
static const char *g_workload_type = NULL;
|
2016-07-20 18:16:23 +00:00
|
|
|
static int g_io_size = 0;
|
|
|
|
/* initialize to invalid value so we can detect if user overrides it. */
|
|
|
|
static int g_rw_percentage = -1;
|
|
|
|
static bool g_verify = false;
|
|
|
|
static bool g_reset = false;
|
2019-11-08 23:30:48 +00:00
|
|
|
static bool g_continue_on_failure = false;
|
2020-05-26 22:05:37 +00:00
|
|
|
static bool g_abort = false;
|
2022-06-29 11:24:51 +00:00
|
|
|
static bool g_error_to_exit = false;
|
2020-02-05 08:02:44 +00:00
|
|
|
static int g_queue_depth = 0;
|
2017-12-07 09:21:31 +00:00
|
|
|
static uint64_t g_time_in_usec;
|
2016-07-20 18:16:23 +00:00
|
|
|
static int g_show_performance_real_time = 0;
|
2022-12-06 06:16:33 +00:00
|
|
|
static uint64_t g_show_performance_period_in_usec = SPDK_SEC_TO_USEC;
|
2017-12-12 21:27:17 +00:00
|
|
|
static uint64_t g_show_performance_period_num = 0;
|
2018-03-06 00:13:05 +00:00
|
|
|
static uint64_t g_show_performance_ema_period = 0;
|
2019-12-26 00:50:50 +00:00
|
|
|
static int g_run_rc = 0;
|
2017-12-07 09:21:31 +00:00
|
|
|
static bool g_shutdown = false;
|
2022-07-06 06:16:43 +00:00
|
|
|
static uint64_t g_start_tsc;
|
2017-12-07 09:21:31 +00:00
|
|
|
static uint64_t g_shutdown_tsc;
|
2020-09-18 19:41:57 +00:00
|
|
|
static bool g_zcopy = false;
|
2020-11-30 20:32:45 +00:00
|
|
|
static struct spdk_thread *g_main_thread;
|
2020-02-05 08:02:44 +00:00
|
|
|
static int g_time_in_sec = 0;
|
|
|
|
static bool g_mix_specified = false;
|
2020-03-18 22:21:37 +00:00
|
|
|
static const char *g_job_bdev_name;
|
2019-07-03 13:56:14 +00:00
|
|
|
static bool g_wait_for_tests = false;
|
|
|
|
static struct spdk_jsonrpc_request *g_request = NULL;
|
2020-03-19 23:00:58 +00:00
|
|
|
static bool g_multithread_mode = false;
|
2020-05-27 00:25:56 +00:00
|
|
|
static int g_timeout_in_sec;
|
2020-06-26 08:29:03 +00:00
|
|
|
static struct spdk_conf *g_bdevperf_conf = NULL;
|
|
|
|
static const char *g_bdevperf_conf_file = NULL;
|
2021-05-06 23:12:43 +00:00
|
|
|
static double g_zipf_theta;
|
2023-02-20 10:54:01 +00:00
|
|
|
static bool g_random_map = false;
|
2016-07-20 18:16:23 +00:00
|
|
|
|
2020-06-26 08:29:03 +00:00
|
|
|
static struct spdk_cpuset g_all_cpuset;
|
2016-10-05 17:37:11 +00:00
|
|
|
static struct spdk_poller *g_perf_timer = NULL;
|
2016-07-20 18:16:23 +00:00
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
static void bdevperf_submit_single(struct bdevperf_job *job, struct bdevperf_task *task);
|
2020-01-06 05:53:32 +00:00
|
|
|
static void rpc_perform_tests_cb(void);
|
2016-07-20 18:16:23 +00:00
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
static uint32_t g_bdev_count = 0;
|
|
|
|
static uint32_t g_latency_display_level;
|
|
|
|
|
2023-03-01 07:45:37 +00:00
|
|
|
static bool g_one_thread_per_lcore = false;
|
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
static const double g_latency_cutoffs[] = {
|
|
|
|
0.01,
|
|
|
|
0.10,
|
|
|
|
0.25,
|
|
|
|
0.50,
|
|
|
|
0.75,
|
|
|
|
0.90,
|
|
|
|
0.95,
|
|
|
|
0.98,
|
|
|
|
0.99,
|
|
|
|
0.995,
|
|
|
|
0.999,
|
|
|
|
0.9999,
|
|
|
|
0.99999,
|
|
|
|
0.999999,
|
|
|
|
0.9999999,
|
|
|
|
-1,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct latency_info {
|
|
|
|
uint64_t min;
|
|
|
|
uint64_t max;
|
|
|
|
uint64_t total;
|
|
|
|
};
|
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
struct bdevperf_job {
|
2017-11-20 10:22:45 +00:00
|
|
|
char *name;
|
|
|
|
struct spdk_bdev *bdev;
|
|
|
|
struct spdk_bdev_desc *bdev_desc;
|
|
|
|
struct spdk_io_channel *ch;
|
2020-03-18 22:21:37 +00:00
|
|
|
TAILQ_ENTRY(bdevperf_job) link;
|
2020-03-19 21:27:32 +00:00
|
|
|
struct spdk_thread *thread;
|
|
|
|
|
2020-06-25 12:33:45 +00:00
|
|
|
const char *workload_type;
|
|
|
|
int io_size;
|
|
|
|
int rw_percentage;
|
|
|
|
bool is_random;
|
|
|
|
bool verify;
|
|
|
|
bool reset;
|
|
|
|
bool continue_on_failure;
|
|
|
|
bool unmap;
|
|
|
|
bool write_zeroes;
|
|
|
|
bool flush;
|
|
|
|
bool abort;
|
|
|
|
int queue_depth;
|
2021-05-06 23:18:58 +00:00
|
|
|
unsigned int seed;
|
2020-06-25 12:33:45 +00:00
|
|
|
|
2017-12-18 07:15:32 +00:00
|
|
|
uint64_t io_completed;
|
2020-05-26 04:56:12 +00:00
|
|
|
uint64_t io_failed;
|
2020-05-27 00:25:56 +00:00
|
|
|
uint64_t io_timeout;
|
2018-03-06 00:13:05 +00:00
|
|
|
uint64_t prev_io_completed;
|
|
|
|
double ema_io_per_second;
|
2017-11-20 10:22:45 +00:00
|
|
|
int current_queue_depth;
|
|
|
|
uint64_t size_in_ios;
|
2020-04-08 03:18:22 +00:00
|
|
|
uint64_t ios_base;
|
2017-11-20 10:22:45 +00:00
|
|
|
uint64_t offset_in_ios;
|
|
|
|
uint64_t io_size_blocks;
|
2020-02-05 08:02:44 +00:00
|
|
|
uint64_t buf_size;
|
2019-02-10 23:40:32 +00:00
|
|
|
uint32_t dif_check_flags;
|
2017-11-20 10:22:45 +00:00
|
|
|
bool is_draining;
|
|
|
|
struct spdk_poller *run_timer;
|
|
|
|
struct spdk_poller *reset_timer;
|
2020-03-06 14:57:22 +00:00
|
|
|
struct spdk_bit_array *outstanding;
|
2021-05-06 23:12:43 +00:00
|
|
|
struct spdk_zipf *zipf;
|
2017-11-20 10:22:45 +00:00
|
|
|
TAILQ_HEAD(, bdevperf_task) task_list;
|
2022-06-29 11:24:51 +00:00
|
|
|
uint64_t run_time_in_usec;
|
2022-09-13 10:07:09 +00:00
|
|
|
|
|
|
|
/* keep channel's histogram data before being destroyed */
|
|
|
|
struct spdk_histogram_data *histogram;
|
2023-02-20 10:54:01 +00:00
|
|
|
struct spdk_bit_array *random_map;
|
2016-07-20 18:16:23 +00:00
|
|
|
};
|
|
|
|
|
2020-01-05 23:18:10 +00:00
|
|
|
struct spdk_bdevperf {
|
2020-03-19 21:27:32 +00:00
|
|
|
TAILQ_HEAD(, bdevperf_job) jobs;
|
2020-03-19 23:05:17 +00:00
|
|
|
uint32_t running_jobs;
|
2020-01-05 23:18:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct spdk_bdevperf g_bdevperf = {
|
2020-03-19 21:27:32 +00:00
|
|
|
.jobs = TAILQ_HEAD_INITIALIZER(g_bdevperf.jobs),
|
2020-03-19 23:05:17 +00:00
|
|
|
.running_jobs = 0,
|
2020-01-05 23:18:10 +00:00
|
|
|
};
|
|
|
|
|
2020-07-09 20:33:15 +00:00
|
|
|
enum job_config_rw {
|
|
|
|
JOB_CONFIG_RW_READ = 0,
|
|
|
|
JOB_CONFIG_RW_WRITE,
|
|
|
|
JOB_CONFIG_RW_RANDREAD,
|
|
|
|
JOB_CONFIG_RW_RANDWRITE,
|
|
|
|
JOB_CONFIG_RW_RW,
|
|
|
|
JOB_CONFIG_RW_RANDRW,
|
|
|
|
JOB_CONFIG_RW_VERIFY,
|
|
|
|
JOB_CONFIG_RW_RESET,
|
|
|
|
JOB_CONFIG_RW_UNMAP,
|
|
|
|
JOB_CONFIG_RW_FLUSH,
|
|
|
|
JOB_CONFIG_RW_WRITE_ZEROES,
|
|
|
|
};
|
|
|
|
|
2020-06-26 08:29:03 +00:00
|
|
|
/* Storing values from a section of job config file */
|
|
|
|
struct job_config {
|
|
|
|
const char *name;
|
|
|
|
const char *filename;
|
|
|
|
struct spdk_cpuset cpumask;
|
|
|
|
int bs;
|
|
|
|
int iodepth;
|
|
|
|
int rwmixread;
|
2023-03-01 07:45:37 +00:00
|
|
|
uint32_t lcore;
|
2020-08-17 09:40:47 +00:00
|
|
|
int64_t offset;
|
2021-08-19 10:02:06 +00:00
|
|
|
uint64_t length;
|
2020-07-09 20:33:15 +00:00
|
|
|
enum job_config_rw rw;
|
2020-06-26 08:29:03 +00:00
|
|
|
TAILQ_ENTRY(job_config) link;
|
|
|
|
};
|
|
|
|
|
|
|
|
TAILQ_HEAD(, job_config) job_config_list
|
|
|
|
= TAILQ_HEAD_INITIALIZER(job_config_list);
|
|
|
|
|
2020-04-14 20:38:19 +00:00
|
|
|
static bool g_performance_dump_active = false;
|
|
|
|
|
2020-04-15 18:43:34 +00:00
|
|
|
struct bdevperf_aggregate_stats {
|
2020-03-19 21:27:32 +00:00
|
|
|
struct bdevperf_job *current_job;
|
2020-04-15 18:15:15 +00:00
|
|
|
uint64_t io_time_in_usec;
|
|
|
|
uint64_t ema_period;
|
|
|
|
double total_io_per_second;
|
|
|
|
double total_mb_per_second;
|
2020-05-26 04:56:12 +00:00
|
|
|
double total_failed_per_second;
|
2020-05-27 00:25:56 +00:00
|
|
|
double total_timeout_per_second;
|
2022-09-13 10:07:09 +00:00
|
|
|
double min_latency;
|
|
|
|
double max_latency;
|
|
|
|
uint64_t total_io_completed;
|
|
|
|
uint64_t total_tsc;
|
2020-04-15 18:15:15 +00:00
|
|
|
};
|
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
static struct bdevperf_aggregate_stats g_stats = {.min_latency = (double)UINT64_MAX};
|
2020-04-14 20:38:19 +00:00
|
|
|
|
2023-03-01 07:45:37 +00:00
|
|
|
struct lcore_thread {
|
|
|
|
struct spdk_thread *thread;
|
|
|
|
uint32_t lcore;
|
|
|
|
TAILQ_ENTRY(lcore_thread) link;
|
|
|
|
};
|
|
|
|
|
|
|
|
TAILQ_HEAD(, lcore_thread) g_lcore_thread_list
|
|
|
|
= TAILQ_HEAD_INITIALIZER(g_lcore_thread_list);
|
|
|
|
|
2020-04-15 18:15:15 +00: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 bdevperf_job *job, uint64_t io_time_in_usec)
|
|
|
|
{
|
2022-12-06 06:16:33 +00:00
|
|
|
return (double)job->io_completed * SPDK_SEC_TO_USEC / io_time_in_usec;
|
2020-04-15 18:15:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static double
|
|
|
|
get_ema_io_per_second(struct bdevperf_job *job, uint64_t ema_period)
|
|
|
|
{
|
|
|
|
double io_completed, io_per_second;
|
|
|
|
|
|
|
|
io_completed = job->io_completed;
|
2022-12-06 06:16:33 +00:00
|
|
|
io_per_second = (double)(io_completed - job->prev_io_completed) * SPDK_SEC_TO_USEC
|
2020-04-15 18:15:15 +00:00
|
|
|
/ g_show_performance_period_in_usec;
|
|
|
|
job->prev_io_completed = io_completed;
|
|
|
|
|
|
|
|
job->ema_io_per_second += (io_per_second - job->ema_io_per_second) * 2
|
|
|
|
/ (ema_period + 1);
|
|
|
|
return job->ema_io_per_second;
|
|
|
|
}
|
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
static void
|
|
|
|
get_avg_latency(void *ctx, uint64_t start, uint64_t end, uint64_t count,
|
|
|
|
uint64_t total, uint64_t so_far)
|
|
|
|
{
|
|
|
|
struct latency_info *latency_info = ctx;
|
|
|
|
|
|
|
|
if (count == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
latency_info->total += (start + end) / 2 * count;
|
|
|
|
|
|
|
|
if (so_far == count) {
|
|
|
|
latency_info->min = start;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (so_far == total) {
|
|
|
|
latency_info->max = end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-15 18:15:15 +00:00
|
|
|
static void
|
2020-04-15 18:43:34 +00:00
|
|
|
performance_dump_job(struct bdevperf_aggregate_stats *stats, struct bdevperf_job *job)
|
2020-04-15 18:15:15 +00:00
|
|
|
{
|
2020-05-27 00:25:56 +00:00
|
|
|
double io_per_second, mb_per_second, failed_per_second, timeout_per_second;
|
2022-09-13 10:07:09 +00:00
|
|
|
double average_latency = 0.0, min_latency, max_latency;
|
2022-06-29 11:24:51 +00:00
|
|
|
uint64_t time_in_usec;
|
2022-09-13 10:07:09 +00:00
|
|
|
uint64_t tsc_rate;
|
|
|
|
uint64_t total_io;
|
|
|
|
struct latency_info latency_info = {};
|
2020-04-15 18:15:15 +00:00
|
|
|
|
2023-03-01 07:16:50 +00:00
|
|
|
printf("\r Job: %s (Core Mask 0x%s)\n", job->name,
|
2021-02-08 21:23:40 +00:00
|
|
|
spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread)));
|
2022-06-29 11:24:51 +00:00
|
|
|
|
|
|
|
if (job->io_failed > 0 && !job->reset && !job->continue_on_failure) {
|
|
|
|
printf("\r Job: %s ended in about %.2f seconds with error\n",
|
2023-03-01 07:16:50 +00:00
|
|
|
job->name, (double)job->run_time_in_usec / SPDK_SEC_TO_USEC);
|
2022-06-29 11:24:51 +00:00
|
|
|
}
|
2020-11-19 21:04:52 +00:00
|
|
|
if (job->verify) {
|
2021-01-20 17:02:23 +00:00
|
|
|
printf("\t Verification LBA range: start 0x%" PRIx64 " length 0x%" PRIx64 "\n",
|
2020-11-19 21:04:52 +00:00
|
|
|
job->ios_base, job->size_in_ios);
|
|
|
|
}
|
2022-06-29 11:24:51 +00:00
|
|
|
|
|
|
|
if (g_performance_dump_active == true) {
|
|
|
|
/* Use job's actual run time as Job has ended */
|
|
|
|
if (job->io_failed > 0 && !job->continue_on_failure) {
|
|
|
|
time_in_usec = job->run_time_in_usec;
|
|
|
|
} else {
|
|
|
|
time_in_usec = stats->io_time_in_usec;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
time_in_usec = job->run_time_in_usec;
|
|
|
|
}
|
|
|
|
|
2020-04-15 18:43:34 +00:00
|
|
|
if (stats->ema_period == 0) {
|
2022-06-29 11:24:51 +00:00
|
|
|
io_per_second = get_cma_io_per_second(job, time_in_usec);
|
2020-04-15 18:15:15 +00:00
|
|
|
} else {
|
2020-04-15 18:43:34 +00:00
|
|
|
io_per_second = get_ema_io_per_second(job, stats->ema_period);
|
2020-04-15 18:15:15 +00:00
|
|
|
}
|
2022-09-13 10:07:09 +00:00
|
|
|
|
|
|
|
tsc_rate = spdk_get_ticks_hz();
|
2020-06-25 12:33:45 +00:00
|
|
|
mb_per_second = io_per_second * job->io_size / (1024 * 1024);
|
2022-06-29 11:24:51 +00:00
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
spdk_histogram_data_iterate(job->histogram, get_avg_latency, &latency_info);
|
|
|
|
|
|
|
|
total_io = job->io_completed + job->io_failed;
|
|
|
|
if (total_io != 0) {
|
2022-12-06 06:16:33 +00:00
|
|
|
average_latency = (double)latency_info.total / total_io * SPDK_SEC_TO_USEC / tsc_rate;
|
2022-09-13 10:07:09 +00:00
|
|
|
}
|
2022-12-06 06:16:33 +00:00
|
|
|
min_latency = (double)latency_info.min * SPDK_SEC_TO_USEC / tsc_rate;
|
|
|
|
max_latency = (double)latency_info.max * SPDK_SEC_TO_USEC / tsc_rate;
|
2022-09-13 10:07:09 +00:00
|
|
|
|
2022-12-06 06:16:33 +00:00
|
|
|
failed_per_second = (double)job->io_failed * SPDK_SEC_TO_USEC / time_in_usec;
|
|
|
|
timeout_per_second = (double)job->io_timeout * SPDK_SEC_TO_USEC / time_in_usec;
|
2020-05-26 04:56:12 +00:00
|
|
|
|
2022-10-11 09:01:44 +00:00
|
|
|
printf("\t %-20s: %10.2f %10.2f %10.2f",
|
2022-12-06 06:16:33 +00:00
|
|
|
job->name, (float)time_in_usec / SPDK_SEC_TO_USEC, io_per_second, mb_per_second);
|
2022-09-13 10:07:09 +00:00
|
|
|
printf(" %10.2f %8.2f",
|
2022-10-11 09:01:44 +00:00
|
|
|
failed_per_second, timeout_per_second);
|
2022-09-13 10:07:09 +00:00
|
|
|
printf(" %10.2f %10.2f %10.2f\n",
|
|
|
|
average_latency, min_latency, max_latency);
|
2022-10-11 09:01:44 +00:00
|
|
|
|
2020-04-15 18:43:34 +00:00
|
|
|
stats->total_io_per_second += io_per_second;
|
|
|
|
stats->total_mb_per_second += mb_per_second;
|
2020-05-26 04:56:12 +00:00
|
|
|
stats->total_failed_per_second += failed_per_second;
|
2020-05-27 00:25:56 +00:00
|
|
|
stats->total_timeout_per_second += timeout_per_second;
|
2022-09-13 10:07:09 +00:00
|
|
|
stats->total_io_completed += job->io_completed + job->io_failed;
|
|
|
|
stats->total_tsc += latency_info.total;
|
|
|
|
if (min_latency < stats->min_latency) {
|
|
|
|
stats->min_latency = min_latency;
|
|
|
|
}
|
|
|
|
if (max_latency > stats->max_latency) {
|
|
|
|
stats->max_latency = max_latency;
|
|
|
|
}
|
2020-04-15 18:15:15 +00:00
|
|
|
}
|
|
|
|
|
2019-02-10 23:28:02 +00:00
|
|
|
static void
|
2019-04-16 08:17:59 +00:00
|
|
|
generate_data(void *buf, int buf_len, int block_size, void *md_buf, int md_size,
|
2020-11-19 21:04:52 +00:00
|
|
|
int num_blocks)
|
2019-02-10 23:28:02 +00:00
|
|
|
{
|
2020-11-19 21:04:52 +00:00
|
|
|
int offset_blocks = 0, md_offset, data_block_size, inner_offset;
|
2019-02-10 23:28:02 +00:00
|
|
|
|
|
|
|
if (buf_len < num_blocks * block_size) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-16 08:17:59 +00:00
|
|
|
if (md_buf == NULL) {
|
|
|
|
data_block_size = block_size - md_size;
|
|
|
|
md_buf = (char *)buf + data_block_size;
|
|
|
|
md_offset = block_size;
|
|
|
|
} else {
|
|
|
|
data_block_size = block_size;
|
|
|
|
md_offset = md_size;
|
|
|
|
}
|
|
|
|
|
2019-02-10 23:28:02 +00:00
|
|
|
while (offset_blocks < num_blocks) {
|
2020-11-19 21:04:52 +00:00
|
|
|
inner_offset = 0;
|
|
|
|
while (inner_offset < data_block_size) {
|
2021-09-02 15:00:23 +00:00
|
|
|
*(uint32_t *)buf = offset_blocks + inner_offset;
|
2020-11-19 21:04:52 +00:00
|
|
|
inner_offset += sizeof(uint32_t);
|
|
|
|
buf += sizeof(uint32_t);
|
|
|
|
}
|
|
|
|
memset(md_buf, offset_blocks, md_size);
|
2019-04-16 08:17:59 +00:00
|
|
|
md_buf += md_offset;
|
2019-02-10 23:28:02 +00:00
|
|
|
offset_blocks++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-10 04:21:02 +00:00
|
|
|
static bool
|
|
|
|
copy_data(void *wr_buf, int wr_buf_len, void *rd_buf, int rd_buf_len, int block_size,
|
|
|
|
void *wr_md_buf, void *rd_md_buf, int md_size, int num_blocks)
|
|
|
|
{
|
|
|
|
if (wr_buf_len < num_blocks * block_size || rd_buf_len < num_blocks * block_size) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert((wr_md_buf != NULL) == (rd_md_buf != NULL));
|
|
|
|
|
|
|
|
memcpy(wr_buf, rd_buf, block_size * num_blocks);
|
|
|
|
|
|
|
|
if (wr_md_buf != NULL) {
|
|
|
|
memcpy(wr_md_buf, rd_md_buf, md_size * num_blocks);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-02-10 23:28:02 +00:00
|
|
|
static bool
|
2019-04-16 08:17:59 +00:00
|
|
|
verify_data(void *wr_buf, int wr_buf_len, void *rd_buf, int rd_buf_len, int block_size,
|
|
|
|
void *wr_md_buf, void *rd_md_buf, int md_size, int num_blocks, bool md_check)
|
2019-02-10 23:28:02 +00:00
|
|
|
{
|
2019-04-16 08:17:59 +00:00
|
|
|
int offset_blocks = 0, md_offset, data_block_size;
|
2019-02-10 23:28:02 +00:00
|
|
|
|
|
|
|
if (wr_buf_len < num_blocks * block_size || rd_buf_len < num_blocks * block_size) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-04-16 08:17:59 +00:00
|
|
|
assert((wr_md_buf != NULL) == (rd_md_buf != NULL));
|
|
|
|
|
|
|
|
if (wr_md_buf == NULL) {
|
|
|
|
data_block_size = block_size - md_size;
|
|
|
|
wr_md_buf = (char *)wr_buf + data_block_size;
|
|
|
|
rd_md_buf = (char *)rd_buf + data_block_size;
|
|
|
|
md_offset = block_size;
|
|
|
|
} else {
|
|
|
|
data_block_size = block_size;
|
|
|
|
md_offset = md_size;
|
|
|
|
}
|
|
|
|
|
2019-02-10 23:28:02 +00:00
|
|
|
while (offset_blocks < num_blocks) {
|
2019-04-16 08:17:59 +00:00
|
|
|
if (memcmp(wr_buf, rd_buf, data_block_size) != 0) {
|
2019-02-10 23:28:02 +00:00
|
|
|
return false;
|
|
|
|
}
|
2019-04-16 08:17:59 +00:00
|
|
|
|
2019-02-10 23:28:02 +00:00
|
|
|
wr_buf += block_size;
|
|
|
|
rd_buf += block_size;
|
2019-04-16 08:17:59 +00:00
|
|
|
|
|
|
|
if (md_check) {
|
|
|
|
if (memcmp(wr_md_buf, rd_md_buf, md_size) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
wr_md_buf += md_offset;
|
|
|
|
rd_md_buf += md_offset;
|
|
|
|
}
|
|
|
|
|
2019-02-10 23:28:02 +00:00
|
|
|
offset_blocks++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-06-26 08:29:03 +00:00
|
|
|
static void
|
|
|
|
free_job_config(void)
|
|
|
|
{
|
|
|
|
struct job_config *config, *tmp;
|
|
|
|
|
|
|
|
spdk_conf_free(g_bdevperf_conf);
|
|
|
|
g_bdevperf_conf = NULL;
|
|
|
|
|
|
|
|
TAILQ_FOREACH_SAFE(config, &job_config_list, link, tmp) {
|
|
|
|
TAILQ_REMOVE(&job_config_list, config, link);
|
|
|
|
free(config);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-17 04:00:55 +00:00
|
|
|
static void
|
|
|
|
bdevperf_job_free(struct bdevperf_job *job)
|
|
|
|
{
|
2022-09-13 10:07:09 +00:00
|
|
|
spdk_histogram_data_free(job->histogram);
|
2022-11-17 04:00:55 +00:00
|
|
|
spdk_bit_array_free(&job->outstanding);
|
2023-02-20 10:54:01 +00:00
|
|
|
spdk_bit_array_free(&job->random_map);
|
2022-11-17 04:00:55 +00:00
|
|
|
spdk_zipf_free(&job->zipf);
|
|
|
|
free(job->name);
|
|
|
|
free(job);
|
|
|
|
}
|
|
|
|
|
2022-11-17 08:44:22 +00:00
|
|
|
static void
|
|
|
|
job_thread_exit(void *ctx)
|
|
|
|
{
|
|
|
|
spdk_thread_exit(spdk_get_thread());
|
|
|
|
}
|
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
static void
|
|
|
|
check_cutoff(void *ctx, uint64_t start, uint64_t end, uint64_t count,
|
|
|
|
uint64_t total, uint64_t so_far)
|
|
|
|
{
|
|
|
|
double so_far_pct;
|
|
|
|
double **cutoff = ctx;
|
|
|
|
uint64_t tsc_rate;
|
|
|
|
|
|
|
|
if (count == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tsc_rate = spdk_get_ticks_hz();
|
|
|
|
so_far_pct = (double)so_far / total;
|
|
|
|
while (so_far_pct >= **cutoff && **cutoff > 0) {
|
2022-12-06 06:16:33 +00:00
|
|
|
printf("%9.5f%% : %9.3fus\n", **cutoff * 100, (double)end * SPDK_SEC_TO_USEC / tsc_rate);
|
2022-09-13 10:07:09 +00:00
|
|
|
(*cutoff)++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
print_bucket(void *ctx, uint64_t start, uint64_t end, uint64_t count,
|
|
|
|
uint64_t total, uint64_t so_far)
|
|
|
|
{
|
|
|
|
double so_far_pct;
|
|
|
|
uint64_t tsc_rate;
|
|
|
|
|
|
|
|
if (count == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tsc_rate = spdk_get_ticks_hz();
|
|
|
|
so_far_pct = (double)so_far * 100 / total;
|
|
|
|
printf("%9.3f - %9.3f: %9.4f%% (%9ju)\n",
|
2022-12-06 06:16:33 +00:00
|
|
|
(double)start * SPDK_SEC_TO_USEC / tsc_rate,
|
|
|
|
(double)end * SPDK_SEC_TO_USEC / tsc_rate,
|
2022-09-13 10:07:09 +00:00
|
|
|
so_far_pct, count);
|
|
|
|
}
|
|
|
|
|
2020-02-07 04:31:45 +00:00
|
|
|
static void
|
2020-04-15 20:36:22 +00:00
|
|
|
bdevperf_test_done(void *ctx)
|
2020-01-06 04:32:49 +00:00
|
|
|
{
|
2020-04-15 20:36:22 +00:00
|
|
|
struct bdevperf_job *job, *jtmp;
|
|
|
|
struct bdevperf_task *task, *ttmp;
|
2023-03-01 07:45:37 +00:00
|
|
|
struct lcore_thread *lthread, *lttmp;
|
2022-09-13 10:07:09 +00:00
|
|
|
double average_latency = 0.0;
|
2022-06-29 11:24:51 +00:00
|
|
|
uint64_t time_in_usec;
|
2022-09-13 10:07:09 +00:00
|
|
|
int rc;
|
2020-02-10 02:15:22 +00:00
|
|
|
|
2022-06-29 11:24:51 +00:00
|
|
|
if (g_time_in_usec) {
|
2020-04-14 20:38:19 +00:00
|
|
|
g_stats.io_time_in_usec = g_time_in_usec;
|
|
|
|
|
2022-06-29 11:24:51 +00:00
|
|
|
if (!g_run_rc && g_performance_dump_active) {
|
2020-04-14 20:38:19 +00:00
|
|
|
spdk_thread_send_msg(spdk_get_thread(), bdevperf_test_done, NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_show_performance_real_time) {
|
|
|
|
spdk_poller_unregister(&g_perf_timer);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_shutdown) {
|
2022-07-06 06:16:43 +00:00
|
|
|
g_shutdown_tsc = spdk_get_ticks() - g_start_tsc;
|
2022-12-06 06:16:33 +00:00
|
|
|
time_in_usec = g_shutdown_tsc * SPDK_SEC_TO_USEC / spdk_get_ticks_hz();
|
2022-06-29 11:24:51 +00:00
|
|
|
g_time_in_usec = (g_time_in_usec > time_in_usec) ? time_in_usec : g_time_in_usec;
|
2020-04-14 20:38:19 +00:00
|
|
|
printf("Received shutdown signal, test time was about %.6f seconds\n",
|
2022-12-06 06:16:33 +00:00
|
|
|
(double)g_time_in_usec / SPDK_SEC_TO_USEC);
|
2020-04-14 20:38:19 +00:00
|
|
|
}
|
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
printf("\n%*s\n", 107, "Latency(us)");
|
|
|
|
printf("\r %-*s: %10s %10s %10s %10s %8s %10s %10s %10s\n",
|
|
|
|
28, "Device Information", "runtime(s)", "IOPS", "MiB/s", "Fail/s", "TO/s", "Average", "min", "max");
|
2022-10-11 09:01:44 +00:00
|
|
|
|
2020-03-19 21:27:32 +00:00
|
|
|
TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) {
|
|
|
|
performance_dump_job(&g_stats, job);
|
2022-09-13 10:07:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
printf("\r =================================================================================="
|
|
|
|
"=================================\n");
|
|
|
|
printf("\r %-28s: %10s %10.2f %10.2f",
|
|
|
|
"Total", "", g_stats.total_io_per_second, g_stats.total_mb_per_second);
|
|
|
|
printf(" %10.2f %8.2f",
|
|
|
|
g_stats.total_failed_per_second, g_stats.total_timeout_per_second);
|
|
|
|
|
|
|
|
if (g_stats.total_io_completed != 0) {
|
2022-12-06 06:16:33 +00:00
|
|
|
average_latency = ((double)g_stats.total_tsc / g_stats.total_io_completed) * SPDK_SEC_TO_USEC /
|
2022-09-13 10:07:09 +00:00
|
|
|
spdk_get_ticks_hz();
|
|
|
|
}
|
|
|
|
printf(" %10.2f %10.2f %10.2f\n", average_latency, g_stats.min_latency, g_stats.max_latency);
|
|
|
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
if (g_latency_display_level == 0 || g_stats.total_io_completed == 0) {
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("\n Latency summary\n");
|
|
|
|
TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) {
|
|
|
|
printf("\r =============================================\n");
|
2023-03-01 07:16:50 +00:00
|
|
|
printf("\r Job: %s (Core Mask 0x%s)\n", job->name,
|
2022-09-13 10:07:09 +00:00
|
|
|
spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread)));
|
|
|
|
|
|
|
|
const double *cutoff = g_latency_cutoffs;
|
|
|
|
|
|
|
|
spdk_histogram_data_iterate(job->histogram, check_cutoff, &cutoff);
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_latency_display_level == 1) {
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("\r Latency histogram\n");
|
|
|
|
TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) {
|
|
|
|
printf("\r =============================================\n");
|
2023-03-01 07:16:50 +00:00
|
|
|
printf("\r Job: %s (Core Mask 0x%s)\n", job->name,
|
2022-09-13 10:07:09 +00:00
|
|
|
spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread)));
|
|
|
|
|
|
|
|
spdk_histogram_data_iterate(job->histogram, print_bucket, NULL);
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
clean:
|
|
|
|
TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) {
|
|
|
|
TAILQ_REMOVE(&g_bdevperf.jobs, job, link);
|
2020-04-15 20:36:22 +00:00
|
|
|
|
2023-03-01 07:45:37 +00:00
|
|
|
if (!g_one_thread_per_lcore) {
|
|
|
|
spdk_thread_send_msg(job->thread, job_thread_exit, NULL);
|
|
|
|
}
|
2022-11-17 08:44:22 +00:00
|
|
|
|
2020-03-19 21:27:32 +00:00
|
|
|
TAILQ_FOREACH_SAFE(task, &job->task_list, link, ttmp) {
|
|
|
|
TAILQ_REMOVE(&job->task_list, task, link);
|
|
|
|
spdk_free(task->buf);
|
|
|
|
spdk_free(task->md_buf);
|
|
|
|
free(task);
|
|
|
|
}
|
2020-04-15 20:36:22 +00:00
|
|
|
|
2022-11-17 04:00:55 +00:00
|
|
|
bdevperf_job_free(job);
|
2020-04-15 20:36:22 +00:00
|
|
|
}
|
|
|
|
|
2023-03-01 07:45:37 +00:00
|
|
|
if (g_one_thread_per_lcore) {
|
|
|
|
TAILQ_FOREACH_SAFE(lthread, &g_lcore_thread_list, link, lttmp) {
|
|
|
|
TAILQ_REMOVE(&g_lcore_thread_list, lthread, link);
|
|
|
|
spdk_thread_send_msg(lthread->thread, job_thread_exit, NULL);
|
|
|
|
free(lthread);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-29 11:24:51 +00:00
|
|
|
rc = g_run_rc;
|
2020-04-15 20:36:22 +00:00
|
|
|
if (g_request && !g_shutdown) {
|
|
|
|
rpc_perform_tests_cb();
|
2022-06-29 11:24:51 +00:00
|
|
|
if (rc != 0) {
|
|
|
|
spdk_app_stop(rc);
|
|
|
|
}
|
2020-04-15 20:36:22 +00:00
|
|
|
} else {
|
2022-06-29 11:24:51 +00:00
|
|
|
spdk_app_stop(rc);
|
2020-04-15 20:36:22 +00:00
|
|
|
}
|
2020-02-10 02:15:22 +00:00
|
|
|
}
|
2017-01-05 19:44:24 +00:00
|
|
|
|
2020-02-10 02:15:22 +00:00
|
|
|
static void
|
2020-03-19 23:18:28 +00:00
|
|
|
bdevperf_job_end(void *ctx)
|
2020-02-10 02:15:22 +00:00
|
|
|
{
|
2020-11-30 20:32:45 +00:00
|
|
|
assert(g_main_thread == spdk_get_thread());
|
2019-07-03 15:36:15 +00:00
|
|
|
|
2020-04-14 20:38:19 +00:00
|
|
|
if (--g_bdevperf.running_jobs == 0) {
|
2020-03-19 22:26:05 +00:00
|
|
|
bdevperf_test_done(NULL);
|
2016-07-20 18:16:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
static void
|
|
|
|
bdevperf_channel_get_histogram_cb(void *cb_arg, int status, struct spdk_histogram_data *histogram)
|
|
|
|
{
|
|
|
|
struct spdk_histogram_data *job_hist = cb_arg;
|
2023-01-09 07:00:44 +00:00
|
|
|
|
|
|
|
if (status == 0) {
|
|
|
|
spdk_histogram_data_merge(job_hist, histogram);
|
|
|
|
}
|
2022-09-13 10:07:09 +00:00
|
|
|
}
|
|
|
|
|
2022-12-05 15:14:55 +00:00
|
|
|
static void
|
|
|
|
bdevperf_job_empty(struct bdevperf_job *job)
|
|
|
|
{
|
|
|
|
uint64_t end_tsc = 0;
|
|
|
|
|
|
|
|
end_tsc = spdk_get_ticks() - g_start_tsc;
|
|
|
|
job->run_time_in_usec = end_tsc * SPDK_SEC_TO_USEC / spdk_get_ticks_hz();
|
|
|
|
/* keep histogram info before channel is destroyed */
|
|
|
|
spdk_bdev_channel_get_histogram(job->ch, bdevperf_channel_get_histogram_cb,
|
|
|
|
job->histogram);
|
|
|
|
spdk_put_io_channel(job->ch);
|
|
|
|
spdk_bdev_close(job->bdev_desc);
|
|
|
|
spdk_thread_send_msg(g_main_thread, bdevperf_job_end, NULL);
|
|
|
|
}
|
|
|
|
|
2020-11-27 10:59:30 +00:00
|
|
|
static void
|
|
|
|
bdevperf_end_task(struct bdevperf_task *task)
|
|
|
|
{
|
|
|
|
struct bdevperf_job *job = task->job;
|
|
|
|
|
|
|
|
TAILQ_INSERT_TAIL(&job->task_list, task, link);
|
|
|
|
if (job->is_draining) {
|
|
|
|
if (job->current_queue_depth == 0) {
|
2022-12-05 15:14:55 +00:00
|
|
|
bdevperf_job_empty(job);
|
2020-11-27 10:59:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-10 02:36:30 +00:00
|
|
|
static void
|
|
|
|
bdevperf_queue_io_wait_with_cb(struct bdevperf_task *task, spdk_bdev_io_wait_cb cb_fn)
|
|
|
|
{
|
2020-03-18 22:21:37 +00:00
|
|
|
struct bdevperf_job *job = task->job;
|
2019-09-10 02:36:30 +00:00
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
task->bdev_io_wait.bdev = job->bdev;
|
2019-09-10 02:36:30 +00:00
|
|
|
task->bdev_io_wait.cb_fn = cb_fn;
|
|
|
|
task->bdev_io_wait.cb_arg = task;
|
2020-03-18 22:21:37 +00:00
|
|
|
spdk_bdev_queue_io_wait(job->bdev, job->ch, &task->bdev_io_wait);
|
2019-09-10 02:36:30 +00:00
|
|
|
}
|
|
|
|
|
2020-03-19 22:47:03 +00:00
|
|
|
static int
|
|
|
|
bdevperf_job_drain(void *ctx)
|
2020-02-07 04:31:45 +00:00
|
|
|
{
|
2020-03-19 22:47:03 +00:00
|
|
|
struct bdevperf_job *job = ctx;
|
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
spdk_poller_unregister(&job->run_timer);
|
2020-06-25 12:33:45 +00:00
|
|
|
if (job->reset) {
|
2020-03-18 22:21:37 +00:00
|
|
|
spdk_poller_unregister(&job->reset_timer);
|
2020-02-07 04:31:45 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
job->is_draining = true;
|
2020-03-19 22:47:03 +00:00
|
|
|
|
|
|
|
return -1;
|
2020-02-07 04:31:45 +00:00
|
|
|
}
|
|
|
|
|
2022-12-05 15:14:55 +00:00
|
|
|
static int
|
|
|
|
bdevperf_job_drain_timer(void *ctx)
|
|
|
|
{
|
|
|
|
struct bdevperf_job *job = ctx;
|
|
|
|
|
|
|
|
bdevperf_job_drain(ctx);
|
|
|
|
if (job->current_queue_depth == 0) {
|
|
|
|
bdevperf_job_empty(job);
|
|
|
|
}
|
|
|
|
|
|
|
|
return SPDK_POLLER_BUSY;
|
|
|
|
}
|
|
|
|
|
2020-05-26 22:05:37 +00:00
|
|
|
static void
|
|
|
|
bdevperf_abort_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
|
|
|
|
{
|
|
|
|
struct bdevperf_task *task = cb_arg;
|
|
|
|
struct bdevperf_job *job = task->job;
|
|
|
|
|
|
|
|
job->current_queue_depth--;
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
job->io_completed++;
|
|
|
|
} else {
|
|
|
|
job->io_failed++;
|
2020-06-25 12:33:45 +00:00
|
|
|
if (!job->continue_on_failure) {
|
2020-05-26 22:05:37 +00:00
|
|
|
bdevperf_job_drain(job);
|
|
|
|
g_run_rc = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
spdk_bdev_free_io(bdev_io);
|
2020-11-27 10:59:30 +00:00
|
|
|
bdevperf_end_task(task);
|
2020-05-26 22:05:37 +00:00
|
|
|
}
|
|
|
|
|
2022-10-19 12:10:02 +00:00
|
|
|
static int
|
|
|
|
bdevperf_verify_dif(struct bdevperf_task *task, struct iovec *iovs, int iovcnt)
|
|
|
|
{
|
|
|
|
struct bdevperf_job *job = task->job;
|
|
|
|
struct spdk_bdev *bdev = job->bdev;
|
|
|
|
struct spdk_dif_ctx dif_ctx;
|
|
|
|
struct spdk_dif_error err_blk = {};
|
|
|
|
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),
|
|
|
|
job->dif_check_flags,
|
|
|
|
task->offset_blocks, 0, 0, 0, 0);
|
|
|
|
if (rc != 0) {
|
|
|
|
fprintf(stderr, "Initialization of DIF context failed\n");
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (spdk_bdev_is_md_interleaved(bdev)) {
|
|
|
|
rc = spdk_dif_verify(iovs, iovcnt, job->io_size_blocks, &dif_ctx, &err_blk);
|
|
|
|
} else {
|
|
|
|
struct iovec md_iov = {
|
|
|
|
.iov_base = task->md_buf,
|
|
|
|
.iov_len = spdk_bdev_get_md_size(bdev) * job->io_size_blocks,
|
|
|
|
};
|
|
|
|
|
|
|
|
rc = spdk_dix_verify(iovs, iovcnt, &md_iov, job->io_size_blocks, &dif_ctx, &err_blk);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc != 0) {
|
|
|
|
fprintf(stderr, "DIF/DIX error detected. type=%d, offset=%" PRIu32 "\n",
|
|
|
|
err_blk.err_type, err_blk.err_offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2016-07-20 18:16:23 +00:00
|
|
|
static void
|
2017-05-16 20:25:03 +00:00
|
|
|
bdevperf_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
|
2016-07-20 18:16:23 +00:00
|
|
|
{
|
2020-03-18 22:21:37 +00:00
|
|
|
struct bdevperf_job *job;
|
2016-10-05 22:00:20 +00:00
|
|
|
struct bdevperf_task *task = cb_arg;
|
2017-05-04 20:57:07 +00:00
|
|
|
struct iovec *iovs;
|
|
|
|
int iovcnt;
|
2019-04-16 08:17:59 +00:00
|
|
|
bool md_check;
|
2020-04-08 03:18:22 +00:00
|
|
|
uint64_t offset_in_ios;
|
2022-10-19 12:10:02 +00:00
|
|
|
int rc;
|
2016-07-20 18:16:23 +00:00
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
job = task->job;
|
|
|
|
md_check = spdk_bdev_get_dif_type(job->bdev) == SPDK_DIF_DISABLE;
|
2016-10-11 15:34:53 +00:00
|
|
|
|
2022-06-29 11:24:51 +00:00
|
|
|
if (g_error_to_exit == true) {
|
|
|
|
bdevperf_job_drain(job);
|
|
|
|
} else if (!success) {
|
2020-06-25 12:33:45 +00:00
|
|
|
if (!job->reset && !job->continue_on_failure) {
|
2020-03-19 22:47:03 +00:00
|
|
|
bdevperf_job_drain(job);
|
2019-12-26 00:50:50 +00:00
|
|
|
g_run_rc = -1;
|
2022-06-29 11:24:51 +00:00
|
|
|
g_error_to_exit = true;
|
2020-11-17 16:22:36 +00:00
|
|
|
printf("task offset: %" PRIu64 " on job bdev=%s fails\n",
|
2020-03-18 22:21:37 +00:00
|
|
|
task->offset_blocks, job->name);
|
2016-11-14 16:55:20 +00:00
|
|
|
}
|
2020-06-25 12:33:45 +00:00
|
|
|
} else if (job->verify || job->reset) {
|
2017-05-04 20:57:07 +00:00
|
|
|
spdk_bdev_io_get_iovec(bdev_io, &iovs, &iovcnt);
|
|
|
|
assert(iovcnt == 1);
|
|
|
|
assert(iovs != NULL);
|
2020-03-18 22:21:37 +00:00
|
|
|
if (!verify_data(task->buf, job->buf_size, iovs[0].iov_base, iovs[0].iov_len,
|
|
|
|
spdk_bdev_get_block_size(job->bdev),
|
2019-04-16 08:17:59 +00:00
|
|
|
task->md_buf, spdk_bdev_io_get_md_buf(bdev_io),
|
2020-03-18 22:21:37 +00:00
|
|
|
spdk_bdev_get_md_size(job->bdev),
|
|
|
|
job->io_size_blocks, md_check)) {
|
2020-11-17 16:22:36 +00:00
|
|
|
printf("Buffer mismatch! Target: %s Disk Offset: %" PRIu64 "\n", job->name, task->offset_blocks);
|
2020-03-01 19:00:08 +00:00
|
|
|
printf(" First dword expected 0x%x got 0x%x\n", *(int *)task->buf, *(int *)iovs[0].iov_base);
|
2020-03-19 22:47:03 +00:00
|
|
|
bdevperf_job_drain(job);
|
2019-12-26 00:50:50 +00:00
|
|
|
g_run_rc = -1;
|
2016-07-20 18:16:23 +00:00
|
|
|
}
|
2022-10-19 12:10:02 +00:00
|
|
|
} else if (job->dif_check_flags != 0) {
|
|
|
|
if (task->io_type == SPDK_BDEV_IO_TYPE_READ && spdk_bdev_get_md_size(job->bdev) != 0) {
|
|
|
|
spdk_bdev_io_get_iovec(bdev_io, &iovs, &iovcnt);
|
|
|
|
assert(iovcnt == 1);
|
|
|
|
assert(iovs != NULL);
|
|
|
|
rc = bdevperf_verify_dif(task, iovs, iovcnt);
|
|
|
|
if (rc != 0) {
|
|
|
|
printf("DIF error detected. task offset: %" PRIu64 " on job bdev=%s\n",
|
|
|
|
task->offset_blocks, job->name);
|
|
|
|
|
|
|
|
success = false;
|
|
|
|
if (!job->reset && !job->continue_on_failure) {
|
|
|
|
bdevperf_job_drain(job);
|
|
|
|
g_run_rc = -1;
|
|
|
|
g_error_to_exit = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-07-20 18:16:23 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
job->current_queue_depth--;
|
2017-12-13 07:04:29 +00:00
|
|
|
|
|
|
|
if (success) {
|
2020-03-18 22:21:37 +00:00
|
|
|
job->io_completed++;
|
2020-05-26 04:56:12 +00:00
|
|
|
} else {
|
|
|
|
job->io_failed++;
|
2017-12-13 07:04:29 +00:00
|
|
|
}
|
2016-07-20 18:16:23 +00:00
|
|
|
|
2020-06-25 12:33:45 +00:00
|
|
|
if (job->verify) {
|
2020-04-08 03:18:22 +00:00
|
|
|
assert(task->offset_blocks / job->io_size_blocks >= job->ios_base);
|
|
|
|
offset_in_ios = task->offset_blocks / job->io_size_blocks - job->ios_base;
|
|
|
|
|
|
|
|
assert(spdk_bit_array_get(job->outstanding, offset_in_ios) == true);
|
|
|
|
spdk_bit_array_clear(job->outstanding, offset_in_ios);
|
|
|
|
}
|
|
|
|
|
2016-07-20 18:16:23 +00: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.
|
|
|
|
*/
|
2020-03-18 22:21:37 +00:00
|
|
|
if (!job->is_draining) {
|
|
|
|
bdevperf_submit_single(job, task);
|
2018-01-05 22:10:18 +00:00
|
|
|
} else {
|
2020-11-27 10:59:30 +00:00
|
|
|
bdevperf_end_task(task);
|
2016-07-20 18:16:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-06-12 15:11:31 +00:00
|
|
|
bdevperf_verify_submit_read(void *cb_arg)
|
2016-07-20 18:16:23 +00:00
|
|
|
{
|
2020-03-18 22:21:37 +00:00
|
|
|
struct bdevperf_job *job;
|
2016-10-05 22:00:20 +00:00
|
|
|
struct bdevperf_task *task = cb_arg;
|
2018-05-31 13:16:10 +00:00
|
|
|
int rc;
|
2016-07-20 18:16:23 +00:00
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
job = task->job;
|
2016-07-20 18:16:23 +00:00
|
|
|
|
|
|
|
/* Read the data back in */
|
2022-10-20 16:46:26 +00:00
|
|
|
rc = spdk_bdev_read_blocks_with_md(job->bdev_desc, job->ch, NULL, NULL,
|
2020-03-18 22:21:37 +00:00
|
|
|
task->offset_blocks, job->io_size_blocks,
|
2019-04-16 08:17:59 +00:00
|
|
|
bdevperf_complete, task);
|
|
|
|
|
2018-06-12 15:11:31 +00:00
|
|
|
if (rc == -ENOMEM) {
|
2019-09-10 02:36:30 +00:00
|
|
|
bdevperf_queue_io_wait_with_cb(task, bdevperf_verify_submit_read);
|
2018-06-12 15:11:31 +00:00
|
|
|
} else if (rc != 0) {
|
2017-06-05 18:39:38 +00:00
|
|
|
printf("Failed to submit read: %d\n", rc);
|
2020-03-19 22:47:03 +00:00
|
|
|
bdevperf_job_drain(job);
|
2019-12-26 00:50:50 +00:00
|
|
|
g_run_rc = rc;
|
2017-06-05 18:39:38 +00:00
|
|
|
}
|
2018-06-12 15:11:31 +00:00
|
|
|
}
|
2016-07-20 18:16:23 +00:00
|
|
|
|
2018-06-12 15:11:31 +00:00
|
|
|
static void
|
|
|
|
bdevperf_verify_write_complete(struct spdk_bdev_io *bdev_io, bool success,
|
|
|
|
void *cb_arg)
|
|
|
|
{
|
2018-07-16 13:33:05 +00: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 18:16:23 +00:00
|
|
|
}
|
|
|
|
|
2018-06-22 21:22:22 +00:00
|
|
|
static void
|
2019-09-10 02:37:52 +00:00
|
|
|
bdevperf_zcopy_populate_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
|
2018-06-22 21:22:22 +00:00
|
|
|
{
|
|
|
|
if (!success) {
|
|
|
|
bdevperf_complete(bdev_io, success, cb_arg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
spdk_bdev_zcopy_end(bdev_io, false, bdevperf_complete, cb_arg);
|
|
|
|
}
|
|
|
|
|
2019-02-10 23:40:32 +00:00
|
|
|
static int
|
|
|
|
bdevperf_generate_dif(struct bdevperf_task *task)
|
|
|
|
{
|
2020-03-18 22:21:37 +00:00
|
|
|
struct bdevperf_job *job = task->job;
|
|
|
|
struct spdk_bdev *bdev = job->bdev;
|
2019-02-10 23:40:32 +00:00
|
|
|
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),
|
2020-03-18 22:21:37 +00:00
|
|
|
job->dif_check_flags,
|
2019-06-04 05:52:24 +00:00
|
|
|
task->offset_blocks, 0, 0, 0, 0);
|
2019-02-10 23:40:32 +00:00
|
|
|
if (rc != 0) {
|
|
|
|
fprintf(stderr, "Initialization of DIF context failed\n");
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-04-16 08:17:59 +00:00
|
|
|
if (spdk_bdev_is_md_interleaved(bdev)) {
|
2020-03-18 22:21:37 +00:00
|
|
|
rc = spdk_dif_generate(&task->iov, 1, job->io_size_blocks, &dif_ctx);
|
2019-04-16 08:17:59 +00:00
|
|
|
} else {
|
|
|
|
struct iovec md_iov = {
|
|
|
|
.iov_base = task->md_buf,
|
2020-03-18 22:21:37 +00:00
|
|
|
.iov_len = spdk_bdev_get_md_size(bdev) * job->io_size_blocks,
|
2019-04-16 08:17:59 +00:00
|
|
|
};
|
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
rc = spdk_dix_generate(&task->iov, 1, &md_iov, job->io_size_blocks, &dif_ctx);
|
2019-04-16 08:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-02-10 23:40:32 +00:00
|
|
|
if (rc != 0) {
|
2019-04-16 08:17:59 +00:00
|
|
|
fprintf(stderr, "Generation of DIF/DIX failed\n");
|
2019-02-10 23:40:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2018-06-14 08:59:35 +00:00
|
|
|
static void
|
2018-06-12 15:11:31 +00:00
|
|
|
bdevperf_submit_task(void *arg)
|
2018-06-14 08:59:35 +00:00
|
|
|
{
|
2018-06-12 15:11:31 +00:00
|
|
|
struct bdevperf_task *task = arg;
|
2020-03-18 22:21:37 +00:00
|
|
|
struct bdevperf_job *job = task->job;
|
2018-06-14 08:59:35 +00:00
|
|
|
struct spdk_bdev_desc *desc;
|
|
|
|
struct spdk_io_channel *ch;
|
2018-06-14 09:26:33 +00:00
|
|
|
spdk_bdev_io_completion_cb cb_fn;
|
2020-04-08 03:18:22 +00:00
|
|
|
uint64_t offset_in_ios;
|
2019-02-10 23:40:32 +00:00
|
|
|
int rc = 0;
|
2018-06-14 08:59:35 +00:00
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
desc = job->bdev_desc;
|
|
|
|
ch = job->ch;
|
2018-06-14 08:59:35 +00:00
|
|
|
|
2018-06-14 09:03:52 +00:00
|
|
|
switch (task->io_type) {
|
|
|
|
case SPDK_BDEV_IO_TYPE_WRITE:
|
2020-03-18 22:21:37 +00:00
|
|
|
if (spdk_bdev_get_md_size(job->bdev) != 0 && job->dif_check_flags != 0) {
|
2019-02-10 23:40:32 +00:00
|
|
|
rc = bdevperf_generate_dif(task);
|
|
|
|
}
|
|
|
|
if (rc == 0) {
|
2020-06-25 12:33:45 +00:00
|
|
|
cb_fn = (job->verify || job->reset) ? bdevperf_verify_write_complete : bdevperf_complete;
|
2019-04-16 08:17:59 +00:00
|
|
|
|
2019-09-10 04:21:02 +00:00
|
|
|
if (g_zcopy) {
|
2019-09-10 02:44:11 +00:00
|
|
|
spdk_bdev_zcopy_end(task->bdev_io, true, cb_fn, task);
|
|
|
|
return;
|
|
|
|
} else {
|
2022-10-20 16:46:26 +00:00
|
|
|
rc = spdk_bdev_writev_blocks_with_md(desc, ch, &task->iov, 1,
|
|
|
|
task->md_buf,
|
2019-04-16 08:17:59 +00:00
|
|
|
task->offset_blocks,
|
2020-03-18 22:21:37 +00:00
|
|
|
job->io_size_blocks,
|
2019-04-16 08:17:59 +00:00
|
|
|
cb_fn, task);
|
|
|
|
}
|
2019-02-10 23:40:32 +00:00
|
|
|
}
|
2018-06-14 09:03:52 +00:00
|
|
|
break;
|
|
|
|
case SPDK_BDEV_IO_TYPE_FLUSH:
|
|
|
|
rc = spdk_bdev_flush_blocks(desc, ch, task->offset_blocks,
|
2020-03-18 22:21:37 +00:00
|
|
|
job->io_size_blocks, bdevperf_complete, task);
|
2018-06-14 09:03:52 +00:00
|
|
|
break;
|
|
|
|
case SPDK_BDEV_IO_TYPE_UNMAP:
|
|
|
|
rc = spdk_bdev_unmap_blocks(desc, ch, task->offset_blocks,
|
2020-03-18 22:21:37 +00:00
|
|
|
job->io_size_blocks, bdevperf_complete, task);
|
2018-06-14 09:03:52 +00:00
|
|
|
break;
|
2018-08-09 07:11:09 +00:00
|
|
|
case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
|
|
|
|
rc = spdk_bdev_write_zeroes_blocks(desc, ch, task->offset_blocks,
|
2020-03-18 22:21:37 +00:00
|
|
|
job->io_size_blocks, bdevperf_complete, task);
|
2018-08-09 07:11:09 +00:00
|
|
|
break;
|
2018-06-14 09:03:52 +00:00
|
|
|
case SPDK_BDEV_IO_TYPE_READ:
|
2018-06-22 21:22:22 +00:00
|
|
|
if (g_zcopy) {
|
2021-04-12 12:03:51 +00:00
|
|
|
rc = spdk_bdev_zcopy_start(desc, ch, NULL, 0, task->offset_blocks, job->io_size_blocks,
|
2019-09-10 02:37:52 +00:00
|
|
|
true, bdevperf_zcopy_populate_complete, task);
|
2018-06-22 21:22:22 +00:00
|
|
|
} else {
|
2022-10-20 16:46:26 +00:00
|
|
|
rc = spdk_bdev_read_blocks_with_md(desc, ch, task->buf, task->md_buf,
|
|
|
|
task->offset_blocks,
|
|
|
|
job->io_size_blocks,
|
|
|
|
bdevperf_complete, task);
|
2018-06-22 21:22:22 +00:00
|
|
|
}
|
2018-06-14 09:03:52 +00:00
|
|
|
break;
|
2020-05-26 22:05:37 +00:00
|
|
|
case SPDK_BDEV_IO_TYPE_ABORT:
|
|
|
|
rc = spdk_bdev_abort(desc, ch, task->task_to_abort, bdevperf_abort_complete, task);
|
|
|
|
break;
|
2018-06-14 09:03:52 +00:00
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
rc = -EINVAL;
|
|
|
|
break;
|
2018-06-13 14:04:46 +00:00
|
|
|
}
|
|
|
|
|
2018-06-12 15:11:31 +00:00
|
|
|
if (rc == -ENOMEM) {
|
2019-09-10 02:36:30 +00:00
|
|
|
bdevperf_queue_io_wait_with_cb(task, bdevperf_submit_task);
|
2018-06-12 15:11:31 +00:00
|
|
|
return;
|
|
|
|
} else if (rc != 0) {
|
2018-06-13 14:04:46 +00:00
|
|
|
printf("Failed to submit bdev_io: %d\n", rc);
|
2020-06-25 12:33:45 +00:00
|
|
|
if (job->verify) {
|
2020-04-08 03:18:22 +00:00
|
|
|
assert(task->offset_blocks / job->io_size_blocks >= job->ios_base);
|
|
|
|
offset_in_ios = task->offset_blocks / job->io_size_blocks - job->ios_base;
|
|
|
|
|
|
|
|
assert(spdk_bit_array_get(job->outstanding, offset_in_ios) == true);
|
|
|
|
spdk_bit_array_clear(job->outstanding, offset_in_ios);
|
|
|
|
}
|
2020-03-19 22:47:03 +00:00
|
|
|
bdevperf_job_drain(job);
|
2019-12-26 00:50:50 +00:00
|
|
|
g_run_rc = rc;
|
2018-06-13 14:04:46 +00:00
|
|
|
return;
|
2016-07-20 18:16:23 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
job->current_queue_depth++;
|
2016-07-20 18:16:23 +00:00
|
|
|
}
|
|
|
|
|
2019-09-10 02:44:11 +00:00
|
|
|
static void
|
|
|
|
bdevperf_zcopy_get_buf_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
|
|
|
|
{
|
|
|
|
struct bdevperf_task *task = cb_arg;
|
2020-03-18 22:21:37 +00:00
|
|
|
struct bdevperf_job *job = task->job;
|
2019-09-10 04:21:02 +00:00
|
|
|
struct iovec *iovs;
|
|
|
|
int iovcnt;
|
2019-09-10 02:44:11 +00:00
|
|
|
|
|
|
|
if (!success) {
|
2020-03-19 22:47:03 +00:00
|
|
|
bdevperf_job_drain(job);
|
2019-12-26 00:50:50 +00:00
|
|
|
g_run_rc = -1;
|
2019-09-10 02:44:11 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
task->bdev_io = bdev_io;
|
|
|
|
task->io_type = SPDK_BDEV_IO_TYPE_WRITE;
|
|
|
|
|
2020-06-25 12:33:45 +00:00
|
|
|
if (job->verify || job->reset) {
|
|
|
|
/* When job->verify or job->reset is enabled, task->buf is used for
|
2019-09-10 04:21:02 +00:00
|
|
|
* verification of read after write. For write I/O, when zcopy APIs
|
|
|
|
* are used, task->buf cannot be used, and data must be written to
|
|
|
|
* the data buffer allocated underneath bdev layer instead.
|
|
|
|
* Hence we copy task->buf to the allocated data buffer here.
|
|
|
|
*/
|
|
|
|
spdk_bdev_io_get_iovec(bdev_io, &iovs, &iovcnt);
|
|
|
|
assert(iovcnt == 1);
|
|
|
|
assert(iovs != NULL);
|
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
copy_data(iovs[0].iov_base, iovs[0].iov_len, task->buf, job->buf_size,
|
|
|
|
spdk_bdev_get_block_size(job->bdev),
|
2019-09-10 04:21:02 +00:00
|
|
|
spdk_bdev_io_get_md_buf(bdev_io), task->md_buf,
|
2020-03-18 22:21:37 +00:00
|
|
|
spdk_bdev_get_md_size(job->bdev), job->io_size_blocks);
|
2019-09-10 04:21:02 +00:00
|
|
|
}
|
|
|
|
|
2019-09-10 02:44:11 +00:00
|
|
|
bdevperf_submit_task(task);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bdevperf_prep_zcopy_write_task(void *arg)
|
|
|
|
{
|
|
|
|
struct bdevperf_task *task = arg;
|
2020-03-18 22:21:37 +00:00
|
|
|
struct bdevperf_job *job = task->job;
|
2019-09-10 02:44:11 +00:00
|
|
|
int rc;
|
|
|
|
|
2021-04-12 12:03:51 +00:00
|
|
|
rc = spdk_bdev_zcopy_start(job->bdev_desc, job->ch, NULL, 0,
|
2020-03-18 22:21:37 +00:00
|
|
|
task->offset_blocks, job->io_size_blocks,
|
2019-09-10 02:44:11 +00:00
|
|
|
false, bdevperf_zcopy_get_buf_complete, task);
|
|
|
|
if (rc != 0) {
|
|
|
|
assert(rc == -ENOMEM);
|
|
|
|
bdevperf_queue_io_wait_with_cb(task, bdevperf_prep_zcopy_write_task);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
job->current_queue_depth++;
|
2019-09-10 02:44:11 +00:00
|
|
|
}
|
|
|
|
|
2019-12-22 23:03:27 +00:00
|
|
|
static struct bdevperf_task *
|
2020-03-18 22:21:37 +00:00
|
|
|
bdevperf_job_get_task(struct bdevperf_job *job)
|
2019-12-22 23:03:27 +00:00
|
|
|
{
|
|
|
|
struct bdevperf_task *task;
|
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
task = TAILQ_FIRST(&job->task_list);
|
2019-12-22 23:03:27 +00:00
|
|
|
if (!task) {
|
|
|
|
printf("Task allocation failed\n");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
TAILQ_REMOVE(&job->task_list, task, link);
|
2019-12-22 23:03:27 +00:00
|
|
|
return task;
|
|
|
|
}
|
|
|
|
|
2019-09-10 02:40:30 +00:00
|
|
|
static void
|
2020-03-18 22:21:37 +00:00
|
|
|
bdevperf_submit_single(struct bdevperf_job *job, struct bdevperf_task *task)
|
2019-09-10 02:40:30 +00:00
|
|
|
{
|
|
|
|
uint64_t offset_in_ios;
|
2023-02-10 15:30:25 +00:00
|
|
|
uint64_t rand_value;
|
2023-02-20 10:54:01 +00:00
|
|
|
uint32_t first_clear;
|
2019-09-10 02:40:30 +00:00
|
|
|
|
2021-05-06 23:12:43 +00:00
|
|
|
if (job->zipf) {
|
|
|
|
offset_in_ios = spdk_zipf_generate(job->zipf);
|
|
|
|
} else if (job->is_random) {
|
2023-02-10 15:30:25 +00:00
|
|
|
/* RAND_MAX is only INT32_MAX, so use 2 calls to rand_r to
|
|
|
|
* get a large enough value to ensure we are issuing I/O
|
|
|
|
* uniformly across the whole bdev.
|
|
|
|
*/
|
|
|
|
rand_value = (uint64_t)rand_r(&job->seed) * RAND_MAX + rand_r(&job->seed);
|
|
|
|
offset_in_ios = rand_value % job->size_in_ios;
|
2023-02-20 10:54:01 +00:00
|
|
|
|
|
|
|
if (g_random_map) {
|
|
|
|
/* Make sure, that the offset does not exceed the maximum size
|
|
|
|
* of the bit array (verified during job creation)
|
|
|
|
*/
|
|
|
|
assert(offset_in_ios < UINT32_MAX);
|
|
|
|
|
|
|
|
first_clear = spdk_bit_array_find_first_clear(job->random_map, (uint32_t)offset_in_ios);
|
|
|
|
|
|
|
|
if (first_clear == UINT32_MAX) {
|
|
|
|
first_clear = spdk_bit_array_find_first_clear(job->random_map, 0);
|
|
|
|
|
|
|
|
if (first_clear == UINT32_MAX) {
|
|
|
|
/* If there are no more clear bits in the array, we start over
|
|
|
|
* and select the previously selected random value.
|
|
|
|
*/
|
|
|
|
spdk_bit_array_clear_mask(job->random_map);
|
|
|
|
first_clear = (uint32_t)offset_in_ios;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
spdk_bit_array_set(job->random_map, first_clear);
|
|
|
|
|
|
|
|
offset_in_ios = first_clear;
|
|
|
|
}
|
2019-09-10 02:40:30 +00:00
|
|
|
} else {
|
2020-03-18 22:21:37 +00:00
|
|
|
offset_in_ios = job->offset_in_ios++;
|
2020-04-08 03:18:22 +00:00
|
|
|
if (job->offset_in_ios == job->size_in_ios) {
|
|
|
|
job->offset_in_ios = 0;
|
2019-09-10 02:40:30 +00:00
|
|
|
}
|
2020-03-06 14:57:22 +00:00
|
|
|
|
|
|
|
/* Increment of offset_in_ios if there's already an outstanding IO
|
2020-06-25 12:33:45 +00:00
|
|
|
* to that location. We only need this with job->verify as random
|
|
|
|
* offsets are not supported with job->verify at this time.
|
2020-03-06 14:57:22 +00:00
|
|
|
*/
|
2020-06-25 12:33:45 +00:00
|
|
|
if (job->verify) {
|
2020-04-08 03:18:22 +00:00
|
|
|
assert(spdk_bit_array_find_first_clear(job->outstanding, 0) != UINT32_MAX);
|
|
|
|
|
|
|
|
while (spdk_bit_array_get(job->outstanding, offset_in_ios)) {
|
2020-03-18 22:21:37 +00:00
|
|
|
offset_in_ios = job->offset_in_ios++;
|
2020-04-08 03:18:22 +00:00
|
|
|
if (job->offset_in_ios == job->size_in_ios) {
|
|
|
|
job->offset_in_ios = 0;
|
2020-03-06 14:57:22 +00:00
|
|
|
}
|
2020-04-08 03:18:22 +00:00
|
|
|
}
|
2020-03-18 22:21:37 +00:00
|
|
|
spdk_bit_array_set(job->outstanding, offset_in_ios);
|
2020-03-06 14:57:22 +00:00
|
|
|
}
|
2019-09-10 02:40:30 +00:00
|
|
|
}
|
|
|
|
|
2020-03-09 21:52:09 +00:00
|
|
|
/* For multi-thread to same job, offset_in_ios is relative
|
|
|
|
* to the LBA range assigned for that job. job->offset_blocks
|
|
|
|
* is absolute (entire bdev LBA range).
|
|
|
|
*/
|
2020-04-08 03:18:22 +00:00
|
|
|
task->offset_blocks = (offset_in_ios + job->ios_base) * job->io_size_blocks;
|
2020-03-09 21:52:09 +00:00
|
|
|
|
2020-06-25 12:33:45 +00:00
|
|
|
if (job->verify || job->reset) {
|
2020-03-18 22:21:37 +00:00
|
|
|
generate_data(task->buf, job->buf_size,
|
|
|
|
spdk_bdev_get_block_size(job->bdev),
|
|
|
|
task->md_buf, spdk_bdev_get_md_size(job->bdev),
|
2020-11-19 21:04:52 +00:00
|
|
|
job->io_size_blocks);
|
2019-09-10 04:21:02 +00:00
|
|
|
if (g_zcopy) {
|
|
|
|
bdevperf_prep_zcopy_write_task(task);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
task->iov.iov_base = task->buf;
|
2020-03-18 22:21:37 +00:00
|
|
|
task->iov.iov_len = job->buf_size;
|
2019-09-10 04:21:02 +00:00
|
|
|
task->io_type = SPDK_BDEV_IO_TYPE_WRITE;
|
|
|
|
}
|
2020-06-25 12:33:45 +00:00
|
|
|
} else if (job->flush) {
|
2019-09-10 02:40:30 +00:00
|
|
|
task->io_type = SPDK_BDEV_IO_TYPE_FLUSH;
|
2020-06-25 12:33:45 +00:00
|
|
|
} else if (job->unmap) {
|
2019-09-10 02:40:30 +00:00
|
|
|
task->io_type = SPDK_BDEV_IO_TYPE_UNMAP;
|
2020-06-25 12:33:45 +00:00
|
|
|
} else if (job->write_zeroes) {
|
2019-09-10 02:40:30 +00:00
|
|
|
task->io_type = SPDK_BDEV_IO_TYPE_WRITE_ZEROES;
|
2020-06-25 12:33:45 +00:00
|
|
|
} else if ((job->rw_percentage == 100) ||
|
2021-05-06 23:18:58 +00:00
|
|
|
(job->rw_percentage != 0 && ((rand_r(&job->seed) % 100) < job->rw_percentage))) {
|
2019-09-10 02:40:30 +00:00
|
|
|
task->io_type = SPDK_BDEV_IO_TYPE_READ;
|
|
|
|
} else {
|
2019-09-10 02:44:11 +00:00
|
|
|
if (g_zcopy) {
|
|
|
|
bdevperf_prep_zcopy_write_task(task);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
task->iov.iov_base = task->buf;
|
2020-03-18 22:21:37 +00:00
|
|
|
task->iov.iov_len = job->buf_size;
|
2019-09-10 02:44:11 +00:00
|
|
|
task->io_type = SPDK_BDEV_IO_TYPE_WRITE;
|
|
|
|
}
|
2019-09-10 02:40:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bdevperf_submit_task(task);
|
|
|
|
}
|
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
static int reset_job(void *arg);
|
2016-07-20 18:16:23 +00:00
|
|
|
|
|
|
|
static void
|
2017-05-16 20:25:03 +00:00
|
|
|
reset_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
|
2016-07-20 18:16:23 +00:00
|
|
|
{
|
2016-10-05 22:00:20 +00:00
|
|
|
struct bdevperf_task *task = cb_arg;
|
2020-03-18 22:21:37 +00:00
|
|
|
struct bdevperf_job *job = task->job;
|
2016-07-20 18:16:23 +00:00
|
|
|
|
2017-05-16 20:25:03 +00:00
|
|
|
if (!success) {
|
2020-03-18 22:21:37 +00:00
|
|
|
printf("Reset blockdev=%s failed\n", spdk_bdev_get_name(job->bdev));
|
2020-03-19 22:47:03 +00:00
|
|
|
bdevperf_job_drain(job);
|
2019-12-26 00:50:50 +00:00
|
|
|
g_run_rc = -1;
|
2016-07-20 18:16:23 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
TAILQ_INSERT_TAIL(&job->task_list, task, link);
|
2017-06-05 18:39:38 +00:00
|
|
|
spdk_bdev_free_io(bdev_io);
|
2016-07-20 18:16:23 +00:00
|
|
|
|
2020-04-14 06:49:46 +00:00
|
|
|
job->reset_timer = SPDK_POLLER_REGISTER(reset_job, job,
|
2022-12-06 06:16:33 +00:00
|
|
|
10 * SPDK_SEC_TO_USEC);
|
2016-07-20 18:16:23 +00:00
|
|
|
}
|
|
|
|
|
2018-03-13 00:16:47 +00:00
|
|
|
static int
|
2020-03-18 22:21:37 +00:00
|
|
|
reset_job(void *arg)
|
2016-07-20 18:16:23 +00:00
|
|
|
{
|
2020-03-18 22:21:37 +00:00
|
|
|
struct bdevperf_job *job = arg;
|
2019-12-22 23:03:27 +00:00
|
|
|
struct bdevperf_task *task;
|
2017-06-05 18:39:38 +00:00
|
|
|
int rc;
|
2016-07-20 18:16:23 +00:00
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
spdk_poller_unregister(&job->reset_timer);
|
2016-10-05 17:37:11 +00:00
|
|
|
|
2016-07-20 18:16:23 +00:00
|
|
|
/* Do reset. */
|
2020-03-18 22:21:37 +00:00
|
|
|
task = bdevperf_job_get_task(job);
|
|
|
|
rc = spdk_bdev_reset(job->bdev_desc, job->ch,
|
2017-06-05 18:39:38 +00:00
|
|
|
reset_cb, task);
|
|
|
|
if (rc) {
|
|
|
|
printf("Reset failed: %d\n", rc);
|
2020-03-19 22:47:03 +00:00
|
|
|
bdevperf_job_drain(job);
|
2019-12-26 00:50:50 +00:00
|
|
|
g_run_rc = -1;
|
2017-06-05 18:39:38 +00:00
|
|
|
}
|
2018-03-13 00:16:47 +00:00
|
|
|
|
|
|
|
return -1;
|
2016-07-20 18:16:23 +00:00
|
|
|
}
|
|
|
|
|
2020-05-27 00:25:56 +00:00
|
|
|
static void
|
|
|
|
bdevperf_timeout_cb(void *cb_arg, struct spdk_bdev_io *bdev_io)
|
|
|
|
{
|
|
|
|
struct bdevperf_job *job = cb_arg;
|
2020-05-26 22:05:37 +00:00
|
|
|
struct bdevperf_task *task;
|
2020-05-27 00:25:56 +00:00
|
|
|
|
|
|
|
job->io_timeout++;
|
2020-05-26 22:05:37 +00:00
|
|
|
|
2020-06-25 12:33:45 +00:00
|
|
|
if (job->is_draining || !job->abort ||
|
2020-05-26 22:05:37 +00:00
|
|
|
!spdk_bdev_io_type_supported(job->bdev, SPDK_BDEV_IO_TYPE_ABORT)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
task = bdevperf_job_get_task(job);
|
|
|
|
if (task == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
task->task_to_abort = spdk_bdev_io_get_cb_arg(bdev_io);
|
|
|
|
task->io_type = SPDK_BDEV_IO_TYPE_ABORT;
|
|
|
|
|
|
|
|
bdevperf_submit_task(task);
|
2020-05-27 00:25:56 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 21:03:08 +00:00
|
|
|
static void
|
2020-03-19 21:27:32 +00:00
|
|
|
bdevperf_job_run(void *ctx)
|
2020-03-20 21:03:08 +00:00
|
|
|
{
|
2020-03-19 21:27:32 +00:00
|
|
|
struct bdevperf_job *job = ctx;
|
2020-03-20 21:44:23 +00:00
|
|
|
struct bdevperf_task *task;
|
|
|
|
int i;
|
|
|
|
|
2020-03-20 21:03:08 +00:00
|
|
|
/* Submit initial I/O for this job. Each time one
|
|
|
|
* completes, another will be submitted. */
|
|
|
|
|
|
|
|
/* Start a timer to stop this I/O chain when the run is over */
|
2022-12-05 15:14:55 +00:00
|
|
|
job->run_timer = SPDK_POLLER_REGISTER(bdevperf_job_drain_timer, job, g_time_in_usec);
|
2020-06-25 12:33:45 +00:00
|
|
|
if (job->reset) {
|
2020-04-14 06:49:46 +00:00
|
|
|
job->reset_timer = SPDK_POLLER_REGISTER(reset_job, job,
|
2022-12-06 06:16:33 +00:00
|
|
|
10 * SPDK_SEC_TO_USEC);
|
2020-03-20 21:03:08 +00:00
|
|
|
}
|
|
|
|
|
2020-05-27 00:25:56 +00:00
|
|
|
spdk_bdev_set_timeout(job->bdev_desc, g_timeout_in_sec, bdevperf_timeout_cb, job);
|
|
|
|
|
2020-06-25 12:33:45 +00:00
|
|
|
for (i = 0; i < job->queue_depth; i++) {
|
2020-03-20 21:44:23 +00:00
|
|
|
task = bdevperf_job_get_task(job);
|
|
|
|
bdevperf_submit_single(job, task);
|
|
|
|
}
|
2020-03-20 21:03:08 +00:00
|
|
|
}
|
|
|
|
|
2016-07-20 18:16:23 +00:00
|
|
|
static void
|
2020-03-19 21:27:32 +00:00
|
|
|
_performance_dump_done(void *ctx)
|
2016-07-20 18:16:23 +00:00
|
|
|
{
|
2020-03-19 21:27:32 +00:00
|
|
|
struct bdevperf_aggregate_stats *stats = ctx;
|
2022-09-13 10:07:09 +00:00
|
|
|
double average_latency;
|
2020-01-09 02:17:55 +00:00
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
printf("\r =================================================================================="
|
|
|
|
"=================================\n");
|
2022-10-11 09:01:44 +00:00
|
|
|
printf("\r %-28s: %10s %10.2f %10.2f",
|
|
|
|
"Total", "", stats->total_io_per_second, stats->total_mb_per_second);
|
2022-09-13 10:07:09 +00:00
|
|
|
printf(" %10.2f %8.2f",
|
2022-10-11 09:01:44 +00:00
|
|
|
stats->total_failed_per_second, stats->total_timeout_per_second);
|
2022-09-13 10:07:09 +00:00
|
|
|
|
2022-12-06 06:16:33 +00:00
|
|
|
average_latency = ((double)stats->total_tsc / stats->total_io_completed) * SPDK_SEC_TO_USEC /
|
2022-09-13 10:07:09 +00:00
|
|
|
spdk_get_ticks_hz();
|
|
|
|
printf(" %10.2f %10.2f %10.2f\n", average_latency, stats->min_latency, stats->max_latency);
|
|
|
|
printf("\n");
|
|
|
|
|
2020-01-09 02:17:55 +00:00
|
|
|
fflush(stdout);
|
|
|
|
|
2020-04-14 20:38:19 +00:00
|
|
|
g_performance_dump_active = false;
|
2020-03-19 22:26:05 +00:00
|
|
|
|
2020-04-15 18:43:34 +00:00
|
|
|
free(stats);
|
2020-01-09 02:17:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-03-19 21:27:32 +00:00
|
|
|
_performance_dump(void *ctx)
|
2020-01-09 02:17:55 +00:00
|
|
|
{
|
2020-03-19 21:27:32 +00:00
|
|
|
struct bdevperf_aggregate_stats *stats = ctx;
|
2016-07-20 18:16:23 +00:00
|
|
|
|
2020-03-19 21:27:32 +00:00
|
|
|
performance_dump_job(stats, stats->current_job);
|
2020-01-09 02:17:55 +00:00
|
|
|
|
2020-03-19 21:27:32 +00:00
|
|
|
/* This assumes the jobs list is static after start up time.
|
|
|
|
* That's true right now, but if that ever changed this would need a lock. */
|
|
|
|
stats->current_job = TAILQ_NEXT(stats->current_job, link);
|
|
|
|
if (stats->current_job == NULL) {
|
2020-11-30 20:32:45 +00:00
|
|
|
spdk_thread_send_msg(g_main_thread, _performance_dump_done, stats);
|
2020-03-19 21:27:32 +00:00
|
|
|
} else {
|
|
|
|
spdk_thread_send_msg(stats->current_job->thread, _performance_dump, stats);
|
2016-07-20 18:16:23 +00:00
|
|
|
}
|
2020-01-09 02:17:55 +00:00
|
|
|
}
|
|
|
|
|
2020-04-14 20:38:19 +00:00
|
|
|
static int
|
|
|
|
performance_statistics_thread(void *arg)
|
2020-01-09 02:17:55 +00:00
|
|
|
{
|
2020-04-15 18:43:34 +00:00
|
|
|
struct bdevperf_aggregate_stats *stats;
|
2020-01-09 02:17:55 +00:00
|
|
|
|
2020-04-14 20:38:19 +00:00
|
|
|
if (g_performance_dump_active) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_performance_dump_active = true;
|
|
|
|
|
2020-04-15 18:43:34 +00:00
|
|
|
stats = calloc(1, sizeof(*stats));
|
|
|
|
if (stats == NULL) {
|
2020-04-14 20:38:19 +00:00
|
|
|
return -1;
|
2020-01-09 02:17:55 +00:00
|
|
|
}
|
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
stats->min_latency = (double)UINT64_MAX;
|
|
|
|
|
2020-04-14 20:38:19 +00:00
|
|
|
g_show_performance_period_num++;
|
|
|
|
|
|
|
|
stats->io_time_in_usec = g_show_performance_period_num * g_show_performance_period_in_usec;
|
|
|
|
stats->ema_period = g_show_performance_ema_period;
|
2020-01-09 02:17:55 +00:00
|
|
|
|
2020-03-19 21:27:32 +00:00
|
|
|
/* Iterate all of the jobs to gather stats
|
|
|
|
* These jobs will not get removed here until a final performance dump is run,
|
|
|
|
* so this should be safe without locking.
|
|
|
|
*/
|
|
|
|
stats->current_job = TAILQ_FIRST(&g_bdevperf.jobs);
|
|
|
|
if (stats->current_job == NULL) {
|
2020-11-30 20:32:45 +00:00
|
|
|
spdk_thread_send_msg(g_main_thread, _performance_dump_done, stats);
|
2020-03-19 21:27:32 +00:00
|
|
|
} else {
|
|
|
|
spdk_thread_send_msg(stats->current_job->thread, _performance_dump, stats);
|
|
|
|
}
|
|
|
|
|
2018-03-13 00:16:47 +00:00
|
|
|
return -1;
|
2016-07-20 18:16:23 +00:00
|
|
|
}
|
|
|
|
|
2020-02-09 22:18:10 +00:00
|
|
|
static void
|
|
|
|
bdevperf_test(void)
|
|
|
|
{
|
2020-03-19 21:27:32 +00:00
|
|
|
struct bdevperf_job *job;
|
|
|
|
|
2022-12-06 06:16:33 +00:00
|
|
|
printf("Running I/O for %" PRIu64 " seconds...\n", g_time_in_usec / (uint64_t)SPDK_SEC_TO_USEC);
|
2020-02-09 22:18:10 +00:00
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
/* Start a timer to dump performance numbers */
|
2022-07-06 06:16:43 +00:00
|
|
|
g_start_tsc = spdk_get_ticks();
|
2021-09-24 08:10:18 +00:00
|
|
|
if (g_show_performance_real_time && !g_perf_timer) {
|
2022-09-13 10:07:09 +00:00
|
|
|
printf("%*s\n", 107, "Latency(us)");
|
|
|
|
printf("\r %-*s: %10s %10s %10s %10s %8s %10s %10s %10s\n",
|
|
|
|
28, "Device Information", "runtime(s)", "IOPS", "MiB/s", "Fail/s", "TO/s", "Average", "min", "max");
|
2022-10-11 09:01:44 +00:00
|
|
|
|
2020-04-14 06:49:46 +00:00
|
|
|
g_perf_timer = SPDK_POLLER_REGISTER(performance_statistics_thread, NULL,
|
2020-02-09 22:18:10 +00:00
|
|
|
g_show_performance_period_in_usec);
|
|
|
|
}
|
|
|
|
|
2020-03-19 21:27:32 +00:00
|
|
|
/* Iterate jobs to start all I/O */
|
|
|
|
TAILQ_FOREACH(job, &g_bdevperf.jobs, link) {
|
|
|
|
g_bdevperf.running_jobs++;
|
|
|
|
spdk_thread_send_msg(job->thread, bdevperf_job_run, job);
|
|
|
|
}
|
2020-02-09 22:18:10 +00:00
|
|
|
}
|
|
|
|
|
2020-02-07 04:31:45 +00:00
|
|
|
static void
|
2021-03-02 10:33:51 +00:00
|
|
|
bdevperf_bdev_removed(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx)
|
2020-02-07 04:31:45 +00:00
|
|
|
{
|
2021-03-02 10:33:51 +00:00
|
|
|
struct bdevperf_job *job = event_ctx;
|
2020-02-07 04:31:45 +00:00
|
|
|
|
2021-03-02 10:33:51 +00:00
|
|
|
if (SPDK_BDEV_EVENT_REMOVE == type) {
|
|
|
|
bdevperf_job_drain(job);
|
|
|
|
}
|
2020-02-07 04:31:45 +00:00
|
|
|
}
|
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
static void
|
|
|
|
bdevperf_histogram_status_cb(void *cb_arg, int status)
|
|
|
|
{
|
|
|
|
if (status != 0) {
|
|
|
|
g_run_rc = status;
|
|
|
|
if (g_continue_on_failure == false) {
|
|
|
|
g_error_to_exit = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (--g_bdev_count == 0) {
|
|
|
|
if (g_run_rc == 0) {
|
|
|
|
/* Ready to run the test */
|
|
|
|
bdevperf_test();
|
|
|
|
} else {
|
|
|
|
bdevperf_test_done(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-20 22:03:23 +00:00
|
|
|
static uint32_t g_construct_job_count = 0;
|
|
|
|
|
2023-01-27 01:32:47 +00:00
|
|
|
static int
|
|
|
|
_bdevperf_enable_histogram(void *ctx, struct spdk_bdev *bdev)
|
|
|
|
{
|
|
|
|
bool *enable = ctx;
|
|
|
|
|
|
|
|
g_bdev_count++;
|
|
|
|
|
|
|
|
spdk_bdev_histogram_enable(bdev, bdevperf_histogram_status_cb, NULL, *enable);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
static void
|
2023-01-27 01:32:47 +00:00
|
|
|
bdevperf_enable_histogram(bool enable)
|
2022-09-13 10:07:09 +00:00
|
|
|
{
|
|
|
|
struct spdk_bdev *bdev;
|
2023-01-27 01:32:47 +00:00
|
|
|
int rc;
|
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
/* increment initial g_bdev_count so that it will never reach 0 in the middle of iteration */
|
|
|
|
g_bdev_count = 1;
|
|
|
|
|
|
|
|
if (g_job_bdev_name != NULL) {
|
|
|
|
bdev = spdk_bdev_get_by_name(g_job_bdev_name);
|
|
|
|
if (bdev) {
|
2023-01-27 01:32:47 +00:00
|
|
|
rc = _bdevperf_enable_histogram(&enable, bdev);
|
2022-09-13 10:07:09 +00:00
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Unable to find bdev '%s'\n", g_job_bdev_name);
|
2023-01-27 01:32:47 +00:00
|
|
|
rc = -1;
|
2022-09-13 10:07:09 +00:00
|
|
|
}
|
|
|
|
} else {
|
2023-01-27 01:32:47 +00:00
|
|
|
rc = spdk_for_each_bdev_leaf(&enable, _bdevperf_enable_histogram);
|
2022-09-13 10:07:09 +00:00
|
|
|
}
|
|
|
|
|
2023-01-27 01:32:47 +00:00
|
|
|
bdevperf_histogram_status_cb(NULL, rc);
|
2022-09-13 10:07:09 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 22:03:23 +00:00
|
|
|
static void
|
2020-03-23 21:56:02 +00:00
|
|
|
_bdevperf_construct_job_done(void *ctx)
|
2020-03-20 22:03:23 +00:00
|
|
|
{
|
|
|
|
if (--g_construct_job_count == 0) {
|
2020-03-23 21:56:02 +00:00
|
|
|
if (g_run_rc != 0) {
|
|
|
|
/* Something failed. */
|
|
|
|
bdevperf_test_done(NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
/* always enable histogram. */
|
2023-01-27 01:32:47 +00:00
|
|
|
bdevperf_enable_histogram(true);
|
2022-09-29 07:04:55 +00:00
|
|
|
} else if (g_run_rc != 0) {
|
|
|
|
/* Reset error as some jobs constructed right */
|
|
|
|
g_run_rc = 0;
|
|
|
|
if (g_continue_on_failure == false) {
|
|
|
|
g_error_to_exit = true;
|
|
|
|
}
|
2020-03-20 22:03:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-21 15:20:05 +00:00
|
|
|
/* Checkformat will not allow to use inlined type,
|
|
|
|
this is a workaround */
|
|
|
|
typedef struct spdk_thread *spdk_thread_t;
|
|
|
|
|
|
|
|
static spdk_thread_t
|
|
|
|
construct_job_thread(struct spdk_cpuset *cpumask, const char *tag)
|
|
|
|
{
|
2020-06-26 08:29:03 +00:00
|
|
|
struct spdk_cpuset tmp;
|
2020-07-21 15:20:05 +00:00
|
|
|
|
2020-11-30 20:32:45 +00:00
|
|
|
/* This function runs on the main thread. */
|
|
|
|
assert(g_main_thread == spdk_get_thread());
|
2020-07-21 15:20:05 +00:00
|
|
|
|
2020-06-26 08:29:03 +00:00
|
|
|
/* Handle default mask */
|
|
|
|
if (spdk_cpuset_count(cpumask) == 0) {
|
|
|
|
cpumask = &g_all_cpuset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Warn user that mask might need to be changed */
|
|
|
|
spdk_cpuset_copy(&tmp, cpumask);
|
|
|
|
spdk_cpuset_or(&tmp, &g_all_cpuset);
|
|
|
|
if (!spdk_cpuset_equal(&tmp, &g_all_cpuset)) {
|
|
|
|
fprintf(stderr, "cpumask for '%s' is too big\n", tag);
|
|
|
|
}
|
|
|
|
|
2021-02-08 21:35:36 +00:00
|
|
|
return spdk_thread_create(tag, cpumask);
|
2020-07-21 15:20:05 +00:00
|
|
|
}
|
|
|
|
|
2020-07-23 19:30:30 +00:00
|
|
|
static uint32_t
|
|
|
|
_get_next_core(void)
|
|
|
|
{
|
|
|
|
static uint32_t current_core = SPDK_ENV_LCORE_ID_ANY;
|
|
|
|
|
|
|
|
if (current_core == SPDK_ENV_LCORE_ID_ANY) {
|
|
|
|
current_core = spdk_env_get_first_core();
|
|
|
|
return current_core;
|
|
|
|
}
|
|
|
|
|
|
|
|
current_core = spdk_env_get_next_core(current_core);
|
|
|
|
if (current_core == SPDK_ENV_LCORE_ID_ANY) {
|
|
|
|
current_core = spdk_env_get_first_core();
|
|
|
|
}
|
|
|
|
|
|
|
|
return current_core;
|
|
|
|
}
|
|
|
|
|
2020-03-20 22:03:23 +00:00
|
|
|
static void
|
2020-03-23 21:56:02 +00:00
|
|
|
_bdevperf_construct_job(void *ctx)
|
|
|
|
{
|
|
|
|
struct bdevperf_job *job = ctx;
|
|
|
|
int rc;
|
|
|
|
|
2021-03-02 10:33:51 +00:00
|
|
|
rc = spdk_bdev_open_ext(spdk_bdev_get_name(job->bdev), true, bdevperf_bdev_removed, job,
|
|
|
|
&job->bdev_desc);
|
2020-03-23 21:56:02 +00:00
|
|
|
if (rc != 0) {
|
|
|
|
SPDK_ERRLOG("Could not open leaf bdev %s, error=%d\n", spdk_bdev_get_name(job->bdev), rc);
|
|
|
|
g_run_rc = -EINVAL;
|
2020-03-24 21:36:20 +00:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2021-04-20 04:46:28 +00:00
|
|
|
if (g_zcopy) {
|
|
|
|
if (!spdk_bdev_io_type_supported(job->bdev, SPDK_BDEV_IO_TYPE_ZCOPY)) {
|
|
|
|
printf("Test requires ZCOPY but bdev module does not support ZCOPY\n");
|
|
|
|
g_run_rc = -ENOTSUP;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-24 21:36:20 +00:00
|
|
|
job->ch = spdk_bdev_get_io_channel(job->bdev_desc);
|
|
|
|
if (!job->ch) {
|
|
|
|
SPDK_ERRLOG("Could not get io_channel for device %s, error=%d\n", spdk_bdev_get_name(job->bdev),
|
|
|
|
rc);
|
2022-09-29 07:04:55 +00:00
|
|
|
spdk_bdev_close(job->bdev_desc);
|
|
|
|
TAILQ_REMOVE(&g_bdevperf.jobs, job, link);
|
2020-03-24 21:36:20 +00:00
|
|
|
g_run_rc = -ENOMEM;
|
|
|
|
goto end;
|
2020-03-23 21:56:02 +00:00
|
|
|
}
|
|
|
|
|
2020-03-24 21:36:20 +00:00
|
|
|
end:
|
2020-11-30 20:32:45 +00:00
|
|
|
spdk_thread_send_msg(g_main_thread, _bdevperf_construct_job_done, NULL);
|
2020-03-23 21:56:02 +00:00
|
|
|
}
|
|
|
|
|
2020-07-09 20:33:15 +00:00
|
|
|
static void
|
|
|
|
job_init_rw(struct bdevperf_job *job, enum job_config_rw rw)
|
|
|
|
{
|
|
|
|
switch (rw) {
|
|
|
|
case JOB_CONFIG_RW_READ:
|
|
|
|
job->rw_percentage = 100;
|
|
|
|
break;
|
|
|
|
case JOB_CONFIG_RW_WRITE:
|
|
|
|
job->rw_percentage = 0;
|
|
|
|
break;
|
|
|
|
case JOB_CONFIG_RW_RANDREAD:
|
|
|
|
job->is_random = true;
|
|
|
|
job->rw_percentage = 100;
|
2022-10-11 06:12:15 +00:00
|
|
|
job->seed = rand();
|
2020-07-09 20:33:15 +00:00
|
|
|
break;
|
|
|
|
case JOB_CONFIG_RW_RANDWRITE:
|
|
|
|
job->is_random = true;
|
|
|
|
job->rw_percentage = 0;
|
2022-10-11 06:12:15 +00:00
|
|
|
job->seed = rand();
|
2020-07-09 20:33:15 +00:00
|
|
|
break;
|
|
|
|
case JOB_CONFIG_RW_RW:
|
|
|
|
job->is_random = false;
|
|
|
|
break;
|
|
|
|
case JOB_CONFIG_RW_RANDRW:
|
|
|
|
job->is_random = true;
|
2022-10-11 06:12:15 +00:00
|
|
|
job->seed = rand();
|
2020-07-09 20:33:15 +00:00
|
|
|
break;
|
|
|
|
case JOB_CONFIG_RW_VERIFY:
|
|
|
|
job->verify = true;
|
|
|
|
job->rw_percentage = 50;
|
|
|
|
break;
|
|
|
|
case JOB_CONFIG_RW_RESET:
|
|
|
|
job->reset = true;
|
|
|
|
job->verify = true;
|
|
|
|
job->rw_percentage = 50;
|
|
|
|
break;
|
|
|
|
case JOB_CONFIG_RW_UNMAP:
|
|
|
|
job->unmap = true;
|
|
|
|
break;
|
|
|
|
case JOB_CONFIG_RW_FLUSH:
|
|
|
|
job->flush = true;
|
|
|
|
break;
|
|
|
|
case JOB_CONFIG_RW_WRITE_ZEROES:
|
|
|
|
job->write_zeroes = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-23 21:56:02 +00:00
|
|
|
static int
|
2020-06-26 08:29:03 +00:00
|
|
|
bdevperf_construct_job(struct spdk_bdev *bdev, struct job_config *config,
|
2020-07-23 19:30:30 +00:00
|
|
|
struct spdk_thread *thread)
|
2020-02-07 04:31:45 +00:00
|
|
|
{
|
2020-03-18 22:21:37 +00:00
|
|
|
struct bdevperf_job *job;
|
2020-03-20 21:20:54 +00:00
|
|
|
struct bdevperf_task *task;
|
2020-02-07 04:31:45 +00:00
|
|
|
int block_size, data_block_size;
|
2020-03-23 21:56:02 +00:00
|
|
|
int rc;
|
2020-03-20 21:20:54 +00:00
|
|
|
int task_num, n;
|
2020-03-19 21:27:32 +00:00
|
|
|
|
2020-03-23 21:56:02 +00:00
|
|
|
block_size = spdk_bdev_get_block_size(bdev);
|
|
|
|
data_block_size = spdk_bdev_get_data_block_size(bdev);
|
2020-03-23 21:10:51 +00:00
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
job = calloc(1, sizeof(struct bdevperf_job));
|
|
|
|
if (!job) {
|
|
|
|
fprintf(stderr, "Unable to allocate memory for new job.\n");
|
2020-03-23 21:56:02 +00:00
|
|
|
return -ENOMEM;
|
2020-02-07 04:31:45 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
job->name = strdup(spdk_bdev_get_name(bdev));
|
|
|
|
if (!job->name) {
|
|
|
|
fprintf(stderr, "Unable to allocate memory for job name.\n");
|
2022-11-17 04:00:55 +00:00
|
|
|
bdevperf_job_free(job);
|
2020-03-23 21:56:02 +00:00
|
|
|
return -ENOMEM;
|
2020-02-07 04:31:45 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 12:33:45 +00:00
|
|
|
job->workload_type = g_workload_type;
|
2020-07-23 19:30:30 +00:00
|
|
|
job->io_size = config->bs;
|
|
|
|
job->rw_percentage = config->rwmixread;
|
2020-06-25 12:33:45 +00:00
|
|
|
job->continue_on_failure = g_continue_on_failure;
|
2020-07-23 19:30:30 +00:00
|
|
|
job->queue_depth = config->iodepth;
|
2020-03-18 22:21:37 +00:00
|
|
|
job->bdev = bdev;
|
2020-06-25 12:33:45 +00:00
|
|
|
job->io_size_blocks = job->io_size / data_block_size;
|
2020-03-18 22:21:37 +00:00
|
|
|
job->buf_size = job->io_size_blocks * block_size;
|
2022-09-09 07:20:55 +00:00
|
|
|
job->abort = g_abort;
|
2020-07-09 20:33:15 +00:00
|
|
|
job_init_rw(job, config->rw);
|
2020-02-07 04:31:45 +00:00
|
|
|
|
2020-06-25 12:33:45 +00:00
|
|
|
if ((job->io_size % data_block_size) != 0) {
|
|
|
|
SPDK_ERRLOG("IO size (%d) is not multiples of data block size of bdev %s (%"PRIu32")\n",
|
|
|
|
job->io_size, spdk_bdev_get_name(bdev), data_block_size);
|
2022-11-17 04:00:55 +00:00
|
|
|
bdevperf_job_free(job);
|
2020-06-25 12:33:45 +00:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (job->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));
|
2022-11-17 04:00:55 +00:00
|
|
|
bdevperf_job_free(job);
|
2020-06-25 12:33:45 +00:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2020-02-07 04:31:45 +00:00
|
|
|
if (spdk_bdev_is_dif_check_enabled(bdev, SPDK_DIF_CHECK_TYPE_REFTAG)) {
|
2020-03-18 22:21:37 +00:00
|
|
|
job->dif_check_flags |= SPDK_DIF_FLAGS_REFTAG_CHECK;
|
2020-02-07 04:31:45 +00:00
|
|
|
}
|
|
|
|
if (spdk_bdev_is_dif_check_enabled(bdev, SPDK_DIF_CHECK_TYPE_GUARD)) {
|
2020-03-18 22:21:37 +00:00
|
|
|
job->dif_check_flags |= SPDK_DIF_FLAGS_GUARD_CHECK;
|
2020-02-07 04:31:45 +00:00
|
|
|
}
|
|
|
|
|
2020-04-08 03:18:22 +00:00
|
|
|
job->offset_in_ios = 0;
|
2020-02-07 04:31:45 +00:00
|
|
|
|
2020-07-23 19:30:30 +00:00
|
|
|
if (config->length != 0) {
|
2020-03-19 21:27:32 +00:00
|
|
|
/* Use subset of disk */
|
2020-07-23 19:30:30 +00:00
|
|
|
job->size_in_ios = config->length / job->io_size_blocks;
|
|
|
|
job->ios_base = config->offset / job->io_size_blocks;
|
2020-03-09 21:52:09 +00:00
|
|
|
} else {
|
2020-03-19 21:27:32 +00:00
|
|
|
/* Use whole disk */
|
|
|
|
job->size_in_ios = spdk_bdev_get_num_blocks(bdev) / job->io_size_blocks;
|
2020-04-08 03:18:22 +00:00
|
|
|
job->ios_base = 0;
|
2020-03-09 21:52:09 +00:00
|
|
|
}
|
|
|
|
|
2021-05-06 23:12:43 +00:00
|
|
|
if (job->is_random && g_zipf_theta > 0) {
|
|
|
|
job->zipf = spdk_zipf_create(job->size_in_ios, g_zipf_theta, 0);
|
|
|
|
}
|
|
|
|
|
2020-06-25 12:33:45 +00:00
|
|
|
if (job->verify) {
|
2023-02-21 16:18:39 +00:00
|
|
|
if (job->size_in_ios >= UINT32_MAX) {
|
|
|
|
SPDK_ERRLOG("Due to constraints of verify operation, the job storage capacity is too large\n");
|
|
|
|
bdevperf_job_free(job);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2020-03-18 22:21:37 +00:00
|
|
|
job->outstanding = spdk_bit_array_create(job->size_in_ios);
|
|
|
|
if (job->outstanding == NULL) {
|
2020-03-20 22:03:23 +00:00
|
|
|
SPDK_ERRLOG("Could not create outstanding array bitmap for bdev %s\n",
|
|
|
|
spdk_bdev_get_name(bdev));
|
2022-11-17 04:00:55 +00:00
|
|
|
bdevperf_job_free(job);
|
2020-03-23 21:56:02 +00:00
|
|
|
return -ENOMEM;
|
2020-03-06 14:57:22 +00:00
|
|
|
}
|
2022-12-05 15:18:19 +00:00
|
|
|
if (job->queue_depth > (int)job->size_in_ios) {
|
|
|
|
SPDK_WARNLOG("Due to constraints of verify job, queue depth (-q, %d) can't exceed the number of IO "
|
|
|
|
"requests which can be submitted to the bdev %s simultaneously (%"PRIu64"). "
|
|
|
|
"Queue depth is limited to %"PRIu64"\n",
|
|
|
|
job->queue_depth, job->name, job->size_in_ios, job->size_in_ios);
|
|
|
|
job->queue_depth = (int)job->size_in_ios;
|
|
|
|
}
|
2020-03-06 14:57:22 +00:00
|
|
|
}
|
|
|
|
|
2022-09-13 10:07:09 +00:00
|
|
|
job->histogram = spdk_histogram_data_alloc();
|
|
|
|
if (job->histogram == NULL) {
|
|
|
|
fprintf(stderr, "Failed to allocate histogram\n");
|
|
|
|
bdevperf_job_free(job);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2020-03-18 22:21:37 +00:00
|
|
|
TAILQ_INIT(&job->task_list);
|
2020-02-07 04:31:45 +00:00
|
|
|
|
2023-02-20 10:54:01 +00:00
|
|
|
if (g_random_map) {
|
|
|
|
if (job->size_in_ios >= UINT32_MAX) {
|
|
|
|
SPDK_ERRLOG("Due to constraints of the random map, the job storage capacity is too large\n");
|
|
|
|
bdevperf_job_free(job);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
job->random_map = spdk_bit_array_create(job->size_in_ios);
|
|
|
|
if (job->random_map == NULL) {
|
|
|
|
SPDK_ERRLOG("Could not create random_map array bitmap for bdev %s\n",
|
|
|
|
spdk_bdev_get_name(bdev));
|
|
|
|
bdevperf_job_free(job);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 12:33:45 +00:00
|
|
|
task_num = job->queue_depth;
|
|
|
|
if (job->reset) {
|
2020-03-20 21:20:54 +00:00
|
|
|
task_num += 1;
|
|
|
|
}
|
2020-06-25 12:33:45 +00:00
|
|
|
if (job->abort) {
|
|
|
|
task_num += job->queue_depth;
|
2020-05-26 22:05:37 +00:00
|
|
|
}
|
2020-03-20 21:20:54 +00:00
|
|
|
|
2020-03-19 21:27:32 +00:00
|
|
|
TAILQ_INSERT_TAIL(&g_bdevperf.jobs, job, link);
|
2020-04-15 20:36:22 +00:00
|
|
|
|
2020-03-20 21:20:54 +00:00
|
|
|
for (n = 0; n < task_num; n++) {
|
2020-03-23 20:56:55 +00:00
|
|
|
task = calloc(1, sizeof(struct bdevperf_task));
|
|
|
|
if (!task) {
|
|
|
|
fprintf(stderr, "Failed to allocate task from memory\n");
|
2023-03-29 13:44:57 +00:00
|
|
|
spdk_zipf_free(&job->zipf);
|
2020-03-23 21:56:02 +00:00
|
|
|
return -ENOMEM;
|
2020-03-23 20:56:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
task->buf = spdk_zmalloc(job->buf_size, spdk_bdev_get_buf_align(job->bdev), NULL,
|
|
|
|
SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
|
|
|
|
if (!task->buf) {
|
|
|
|
fprintf(stderr, "Cannot allocate buf for task=%p\n", task);
|
2023-03-29 13:44:57 +00:00
|
|
|
spdk_zipf_free(&job->zipf);
|
2020-03-23 20:56:55 +00:00
|
|
|
free(task);
|
2020-03-23 21:56:02 +00:00
|
|
|
return -ENOMEM;
|
2020-03-20 21:20:54 +00:00
|
|
|
}
|
2020-03-23 20:56:55 +00:00
|
|
|
|
|
|
|
if (spdk_bdev_is_md_separate(job->bdev)) {
|
|
|
|
task->md_buf = spdk_zmalloc(job->io_size_blocks *
|
|
|
|
spdk_bdev_get_md_size(job->bdev), 0, NULL,
|
|
|
|
SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
|
|
|
|
if (!task->md_buf) {
|
|
|
|
fprintf(stderr, "Cannot allocate md buf for task=%p\n", task);
|
2023-03-29 13:44:57 +00:00
|
|
|
spdk_zipf_free(&job->zipf);
|
2020-04-15 20:36:22 +00:00
|
|
|
spdk_free(task->buf);
|
2020-03-23 20:56:55 +00:00
|
|
|
free(task);
|
2020-03-23 21:56:02 +00:00
|
|
|
return -ENOMEM;
|
2020-03-23 20:56:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
task->job = job;
|
2020-03-20 21:20:54 +00:00
|
|
|
TAILQ_INSERT_TAIL(&job->task_list, task, link);
|
|
|
|
}
|
|
|
|
|
2020-03-19 21:27:32 +00:00
|
|
|
job->thread = thread;
|
2020-02-09 22:34:28 +00:00
|
|
|
|
2020-03-23 21:56:02 +00:00
|
|
|
g_construct_job_count++;
|
|
|
|
|
2020-03-19 21:27:32 +00:00
|
|
|
rc = spdk_thread_send_msg(thread, _bdevperf_construct_job, job);
|
2020-03-23 21:56:02 +00:00
|
|
|
assert(rc == 0);
|
2020-02-10 02:15:22 +00:00
|
|
|
|
2020-03-23 21:56:02 +00:00
|
|
|
return rc;
|
2020-02-10 02:15:22 +00:00
|
|
|
}
|
|
|
|
|
2020-07-09 20:33:15 +00:00
|
|
|
static int
|
|
|
|
parse_rw(const char *str, enum job_config_rw ret)
|
|
|
|
{
|
|
|
|
if (str == NULL) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(str, "read")) {
|
|
|
|
ret = JOB_CONFIG_RW_READ;
|
|
|
|
} else if (!strcmp(str, "randread")) {
|
|
|
|
ret = JOB_CONFIG_RW_RANDREAD;
|
|
|
|
} else if (!strcmp(str, "write")) {
|
|
|
|
ret = JOB_CONFIG_RW_WRITE;
|
|
|
|
} else if (!strcmp(str, "randwrite")) {
|
|
|
|
ret = JOB_CONFIG_RW_RANDWRITE;
|
|
|
|
} else if (!strcmp(str, "verify")) {
|
|
|
|
ret = JOB_CONFIG_RW_VERIFY;
|
|
|
|
} else if (!strcmp(str, "reset")) {
|
|
|
|
ret = JOB_CONFIG_RW_RESET;
|
|
|
|
} else if (!strcmp(str, "unmap")) {
|
|
|
|
ret = JOB_CONFIG_RW_UNMAP;
|
|
|
|
} else if (!strcmp(str, "write_zeroes")) {
|
|
|
|
ret = JOB_CONFIG_RW_WRITE_ZEROES;
|
|
|
|
} else if (!strcmp(str, "flush")) {
|
|
|
|
ret = JOB_CONFIG_RW_FLUSH;
|
|
|
|
} else if (!strcmp(str, "rw")) {
|
|
|
|
ret = JOB_CONFIG_RW_RW;
|
|
|
|
} else if (!strcmp(str, "randrw")) {
|
|
|
|
ret = JOB_CONFIG_RW_RANDRW;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "rw must be one of\n"
|
|
|
|
"(read, write, randread, randwrite, rw, randrw, verify, reset, unmap, flush)\n");
|
|
|
|
ret = BDEVPERF_CONFIG_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-07-21 16:23:28 +00:00
|
|
|
static const char *
|
|
|
|
config_filename_next(const char *filename, char *out)
|
|
|
|
{
|
|
|
|
int i, k;
|
|
|
|
|
|
|
|
if (filename == NULL) {
|
|
|
|
out[0] = '\0';
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filename[0] == ':') {
|
|
|
|
filename++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0, k = 0;
|
|
|
|
filename[i] != '\0' &&
|
|
|
|
filename[i] != ':' &&
|
|
|
|
i < BDEVPERF_CONFIG_MAX_FILENAME;
|
|
|
|
i++) {
|
|
|
|
if (filename[i] == ' ' || filename[i] == '\t') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
out[k++] = filename[i];
|
|
|
|
}
|
|
|
|
out[k] = 0;
|
|
|
|
|
|
|
|
return filename + i;
|
|
|
|
}
|
|
|
|
|
2023-03-01 07:45:37 +00:00
|
|
|
static struct spdk_thread *
|
|
|
|
get_lcore_thread(uint32_t lcore)
|
|
|
|
{
|
|
|
|
struct lcore_thread *lthread;
|
|
|
|
|
|
|
|
TAILQ_FOREACH(lthread, &g_lcore_thread_list, link) {
|
|
|
|
if (lthread->lcore == lcore) {
|
|
|
|
return lthread->thread;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-06-26 08:29:03 +00:00
|
|
|
static void
|
2021-10-13 20:50:35 +00:00
|
|
|
bdevperf_construct_jobs(void)
|
2020-06-26 08:29:03 +00:00
|
|
|
{
|
2020-07-21 16:23:28 +00:00
|
|
|
char filename[BDEVPERF_CONFIG_MAX_FILENAME];
|
2020-06-26 08:29:03 +00:00
|
|
|
struct spdk_thread *thread;
|
|
|
|
struct job_config *config;
|
|
|
|
struct spdk_bdev *bdev;
|
2020-07-21 16:23:28 +00:00
|
|
|
const char *filenames;
|
2020-06-26 08:29:03 +00:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
TAILQ_FOREACH(config, &job_config_list, link) {
|
2020-07-21 16:23:28 +00:00
|
|
|
filenames = config->filename;
|
2020-06-26 08:29:03 +00:00
|
|
|
|
2023-03-01 07:45:37 +00:00
|
|
|
if (!g_one_thread_per_lcore) {
|
|
|
|
thread = construct_job_thread(&config->cpumask, config->name);
|
|
|
|
} else {
|
|
|
|
thread = get_lcore_thread(config->lcore);
|
|
|
|
}
|
2020-06-26 08:29:03 +00:00
|
|
|
assert(thread);
|
|
|
|
|
2020-07-21 16:23:28 +00:00
|
|
|
while (filenames) {
|
|
|
|
filenames = config_filename_next(filenames, filename);
|
|
|
|
if (strlen(filename) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bdev = spdk_bdev_get_by_name(filename);
|
|
|
|
if (!bdev) {
|
|
|
|
fprintf(stderr, "Unable to find bdev '%s'\n", filename);
|
|
|
|
g_run_rc = -EINVAL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = bdevperf_construct_job(bdev, config, thread);
|
|
|
|
if (rc < 0) {
|
|
|
|
g_run_rc = rc;
|
|
|
|
return;
|
|
|
|
}
|
2020-06-26 08:29:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 19:30:30 +00:00
|
|
|
static int
|
2021-08-19 10:02:06 +00:00
|
|
|
make_cli_job_config(const char *filename, int64_t offset, uint64_t range)
|
2020-07-23 19:30:30 +00:00
|
|
|
{
|
|
|
|
struct job_config *config = calloc(1, sizeof(*config));
|
|
|
|
|
|
|
|
if (config == NULL) {
|
|
|
|
fprintf(stderr, "Unable to allocate memory for job config\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
config->name = filename;
|
|
|
|
config->filename = filename;
|
2023-03-01 07:45:37 +00:00
|
|
|
config->lcore = _get_next_core();
|
2020-07-23 19:30:30 +00:00
|
|
|
spdk_cpuset_zero(&config->cpumask);
|
2023-03-01 07:45:37 +00:00
|
|
|
spdk_cpuset_set_cpu(&config->cpumask, config->lcore, true);
|
2020-07-23 19:30:30 +00:00
|
|
|
config->bs = g_io_size;
|
|
|
|
config->iodepth = g_queue_depth;
|
|
|
|
config->rwmixread = g_rw_percentage;
|
|
|
|
config->offset = offset;
|
|
|
|
config->length = range;
|
2020-07-09 20:33:15 +00:00
|
|
|
config->rw = parse_rw(g_workload_type, BDEVPERF_CONFIG_ERROR);
|
|
|
|
if ((int)config->rw == BDEVPERF_CONFIG_ERROR) {
|
2022-11-21 04:44:16 +00:00
|
|
|
free(config);
|
2020-07-09 20:33:15 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2020-07-23 19:30:30 +00:00
|
|
|
|
|
|
|
TAILQ_INSERT_TAIL(&job_config_list, config, link);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-01-20 04:37:08 +00:00
|
|
|
static int
|
2023-01-27 01:32:47 +00:00
|
|
|
bdevperf_construct_multithread_job_config(void *ctx, struct spdk_bdev *bdev)
|
2023-01-20 04:37:08 +00:00
|
|
|
{
|
2023-01-27 01:32:47 +00:00
|
|
|
uint32_t *num_cores = ctx;
|
2023-01-20 04:37:08 +00:00
|
|
|
uint32_t i;
|
|
|
|
uint64_t blocks_per_job;
|
|
|
|
int64_t offset;
|
|
|
|
int rc;
|
|
|
|
|
2023-01-27 01:32:47 +00:00
|
|
|
blocks_per_job = spdk_bdev_get_num_blocks(bdev) / *num_cores;
|
2023-01-20 04:37:08 +00:00
|
|
|
offset = 0;
|
|
|
|
|
|
|
|
SPDK_ENV_FOREACH_CORE(i) {
|
|
|
|
rc = make_cli_job_config(spdk_bdev_get_name(bdev), offset, blocks_per_job);
|
|
|
|
if (rc) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += blocks_per_job;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-23 21:56:02 +00:00
|
|
|
static void
|
2021-10-13 20:50:35 +00:00
|
|
|
bdevperf_construct_multithread_job_configs(void)
|
2020-03-23 21:56:02 +00:00
|
|
|
{
|
|
|
|
struct spdk_bdev *bdev;
|
2020-03-19 21:27:32 +00:00
|
|
|
uint32_t i;
|
|
|
|
uint32_t num_cores;
|
2020-03-23 21:56:02 +00:00
|
|
|
|
2020-03-19 21:27:32 +00:00
|
|
|
num_cores = 0;
|
|
|
|
SPDK_ENV_FOREACH_CORE(i) {
|
|
|
|
num_cores++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_cores == 0) {
|
|
|
|
g_run_rc = -EINVAL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-23 21:56:02 +00:00
|
|
|
if (g_job_bdev_name != NULL) {
|
|
|
|
bdev = spdk_bdev_get_by_name(g_job_bdev_name);
|
|
|
|
if (!bdev) {
|
|
|
|
fprintf(stderr, "Unable to find bdev '%s'\n", g_job_bdev_name);
|
|
|
|
return;
|
|
|
|
}
|
2023-01-27 01:32:47 +00:00
|
|
|
g_run_rc = bdevperf_construct_multithread_job_config(&num_cores, bdev);
|
2020-03-23 21:56:02 +00:00
|
|
|
} else {
|
2023-01-27 01:32:47 +00:00
|
|
|
g_run_rc = spdk_for_each_bdev_leaf(&num_cores, bdevperf_construct_multithread_job_config);
|
2020-03-23 21:56:02 +00:00
|
|
|
}
|
2023-01-27 01:32:47 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bdevperf_construct_job_config(void *ctx, struct spdk_bdev *bdev)
|
|
|
|
{
|
|
|
|
/* Construct the job */
|
|
|
|
return make_cli_job_config(spdk_bdev_get_name(bdev), 0, 0);
|
2020-03-23 21:56:02 +00:00
|
|
|
}
|
|
|
|
|
2023-03-01 07:45:37 +00:00
|
|
|
static void
|
|
|
|
create_lcore_thread(uint32_t lcore)
|
|
|
|
{
|
|
|
|
struct lcore_thread *lthread;
|
|
|
|
struct spdk_cpuset cpumask = {};
|
|
|
|
char name[32];
|
|
|
|
|
|
|
|
lthread = calloc(1, sizeof(*lthread));
|
|
|
|
assert(lthread != NULL);
|
|
|
|
|
|
|
|
lthread->lcore = lcore;
|
|
|
|
|
|
|
|
snprintf(name, sizeof(name), "lcore_%u", lcore);
|
|
|
|
spdk_cpuset_set_cpu(&cpumask, lcore, true);
|
|
|
|
|
|
|
|
lthread->thread = spdk_thread_create(name, &cpumask);
|
|
|
|
assert(lthread->thread != NULL);
|
|
|
|
|
|
|
|
TAILQ_INSERT_TAIL(&g_lcore_thread_list, lthread, link);
|
|
|
|
}
|
|
|
|
|
2020-02-09 22:26:31 +00:00
|
|
|
static void
|
2021-10-13 20:50:35 +00:00
|
|
|
bdevperf_construct_job_configs(void)
|
2020-02-09 22:26:31 +00:00
|
|
|
{
|
|
|
|
struct spdk_bdev *bdev;
|
2023-03-01 07:45:37 +00:00
|
|
|
uint32_t i;
|
2020-03-23 21:56:02 +00:00
|
|
|
|
2020-06-26 08:29:03 +00:00
|
|
|
/* There are three different modes for allocating jobs. Standard mode
|
2020-03-19 21:27:32 +00:00
|
|
|
* (the default) creates one spdk_thread per bdev and runs the I/O job there.
|
2020-03-23 21:56:02 +00:00
|
|
|
*
|
|
|
|
* The -C flag places bdevperf into "multithread" mode, meaning it creates
|
2020-03-19 21:27:32 +00:00
|
|
|
* one spdk_thread per bdev PER CORE, and runs a copy of the job on each.
|
2020-03-23 21:56:02 +00:00
|
|
|
* This runs multiple threads per bdev, effectively.
|
2020-06-26 08:29:03 +00:00
|
|
|
*
|
|
|
|
* The -j flag implies "FIO" mode which tries to mimic semantic of FIO jobs.
|
|
|
|
* In "FIO" mode, threads are spawned per-job instead of per-bdev.
|
|
|
|
* Each FIO job can be individually parameterized by filename, cpu mask, etc,
|
|
|
|
* which is different from other modes in that they only support global options.
|
2023-03-01 07:45:37 +00:00
|
|
|
*
|
|
|
|
* Both for standard mode and "multithread" mode, if the -E flag is specified,
|
|
|
|
* it creates one spdk_thread PER CORE. On each core, one spdk_thread is shared by
|
|
|
|
* multiple jobs.
|
2020-03-23 21:56:02 +00:00
|
|
|
*/
|
2020-02-09 22:26:31 +00:00
|
|
|
|
2020-06-26 08:29:03 +00:00
|
|
|
if (g_bdevperf_conf) {
|
|
|
|
goto end;
|
2020-03-23 21:56:02 +00:00
|
|
|
}
|
|
|
|
|
2023-03-01 07:45:37 +00:00
|
|
|
if (g_one_thread_per_lcore) {
|
|
|
|
SPDK_ENV_FOREACH_CORE(i) {
|
|
|
|
create_lcore_thread(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_multithread_mode) {
|
|
|
|
bdevperf_construct_multithread_job_configs();
|
|
|
|
} else if (g_job_bdev_name != NULL) {
|
2020-03-18 22:21:37 +00:00
|
|
|
bdev = spdk_bdev_get_by_name(g_job_bdev_name);
|
2020-02-09 22:26:31 +00:00
|
|
|
if (bdev) {
|
2020-03-23 21:56:02 +00:00
|
|
|
/* Construct the job */
|
2020-07-23 19:30:30 +00:00
|
|
|
g_run_rc = make_cli_job_config(g_job_bdev_name, 0, 0);
|
2020-02-09 22:26:31 +00:00
|
|
|
} else {
|
2020-03-18 22:21:37 +00:00
|
|
|
fprintf(stderr, "Unable to find bdev '%s'\n", g_job_bdev_name);
|
2020-02-07 04:31:45 +00:00
|
|
|
}
|
|
|
|
} else {
|
2023-01-27 01:32:47 +00:00
|
|
|
g_run_rc = spdk_for_each_bdev_leaf(NULL, bdevperf_construct_job_config);
|
2020-02-07 04:31:45 +00:00
|
|
|
}
|
2020-02-07 04:40:15 +00:00
|
|
|
|
2020-03-23 21:56:02 +00:00
|
|
|
end:
|
2021-10-13 20:50:35 +00:00
|
|
|
/* Increment initial construct_jobs count so that it will never reach 0 in the middle
|
|
|
|
* of iteration.
|
|
|
|
*/
|
|
|
|
g_construct_job_count = 1;
|
|
|
|
|
2020-07-23 19:30:30 +00:00
|
|
|
if (g_run_rc == 0) {
|
2021-10-13 20:50:35 +00:00
|
|
|
bdevperf_construct_jobs();
|
2020-07-23 19:30:30 +00:00
|
|
|
}
|
|
|
|
|
2020-11-20 14:14:38 +00:00
|
|
|
_bdevperf_construct_job_done(NULL);
|
2020-02-07 04:31:45 +00:00
|
|
|
}
|
|
|
|
|
2020-06-26 08:29:03 +00:00
|
|
|
static int
|
|
|
|
parse_uint_option(struct spdk_conf_section *s, const char *name, int def)
|
|
|
|
{
|
|
|
|
const char *job_name;
|
|
|
|
int tmp;
|
|
|
|
|
|
|
|
tmp = spdk_conf_section_get_intval(s, name);
|
|
|
|
if (tmp == -1) {
|
|
|
|
/* Field was not found. Check default value
|
|
|
|
* In [global] section it is ok to have undefined values
|
|
|
|
* but for other sections it is not ok */
|
|
|
|
if (def == BDEVPERF_CONFIG_UNDEFINED) {
|
|
|
|
job_name = spdk_conf_section_get_name(s);
|
|
|
|
if (strcmp(job_name, "global") == 0) {
|
|
|
|
return def;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr,
|
|
|
|
"Job '%s' has no '%s' assigned\n",
|
|
|
|
job_name, name);
|
|
|
|
return BDEVPERF_CONFIG_ERROR;
|
|
|
|
}
|
|
|
|
return def;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NOTE: get_intval returns nonnegative on success */
|
|
|
|
if (tmp < 0) {
|
|
|
|
fprintf(stderr, "Job '%s' has bad '%s' value.\n",
|
|
|
|
spdk_conf_section_get_name(s), name);
|
|
|
|
return BDEVPERF_CONFIG_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* CLI arguments override parameters for global sections */
|
|
|
|
static void
|
|
|
|
config_set_cli_args(struct job_config *config)
|
|
|
|
{
|
2020-07-20 21:10:38 +00:00
|
|
|
if (g_job_bdev_name) {
|
|
|
|
config->filename = g_job_bdev_name;
|
|
|
|
}
|
2020-06-26 08:29:03 +00:00
|
|
|
if (g_io_size > 0) {
|
|
|
|
config->bs = g_io_size;
|
|
|
|
}
|
|
|
|
if (g_queue_depth > 0) {
|
|
|
|
config->iodepth = g_queue_depth;
|
|
|
|
}
|
|
|
|
if (g_rw_percentage > 0) {
|
|
|
|
config->rwmixread = g_rw_percentage;
|
|
|
|
}
|
2020-07-09 20:33:15 +00:00
|
|
|
if (g_workload_type) {
|
|
|
|
config->rw = parse_rw(g_workload_type, config->rw);
|
|
|
|
}
|
2020-06-26 08:29:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
read_job_config(void)
|
|
|
|
{
|
|
|
|
struct job_config global_default_config;
|
|
|
|
struct job_config global_config;
|
|
|
|
struct spdk_conf_section *s;
|
|
|
|
struct job_config *config;
|
|
|
|
const char *cpumask;
|
2020-07-09 20:33:15 +00:00
|
|
|
const char *rw;
|
2020-06-26 08:29:03 +00:00
|
|
|
bool is_global;
|
|
|
|
int n = 0;
|
2021-08-19 10:02:06 +00:00
|
|
|
int val;
|
2020-06-26 08:29:03 +00:00
|
|
|
|
|
|
|
if (g_bdevperf_conf_file == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_bdevperf_conf = spdk_conf_allocate();
|
|
|
|
if (g_bdevperf_conf == NULL) {
|
|
|
|
fprintf(stderr, "Could not allocate job config structure\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
spdk_conf_disable_sections_merge(g_bdevperf_conf);
|
|
|
|
if (spdk_conf_read(g_bdevperf_conf, g_bdevperf_conf_file)) {
|
|
|
|
fprintf(stderr, "Invalid job config");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize global defaults */
|
|
|
|
global_default_config.filename = NULL;
|
|
|
|
/* Zero mask is the same as g_all_cpuset
|
|
|
|
* The g_all_cpuset is not initialized yet,
|
|
|
|
* so use zero mask as the default instead */
|
|
|
|
spdk_cpuset_zero(&global_default_config.cpumask);
|
|
|
|
global_default_config.bs = BDEVPERF_CONFIG_UNDEFINED;
|
|
|
|
global_default_config.iodepth = BDEVPERF_CONFIG_UNDEFINED;
|
|
|
|
/* bdevperf has no default for -M option but in FIO the default is 50 */
|
|
|
|
global_default_config.rwmixread = 50;
|
2020-07-23 19:03:36 +00:00
|
|
|
global_default_config.offset = 0;
|
|
|
|
/* length 0 means 100% */
|
|
|
|
global_default_config.length = 0;
|
2020-07-09 20:33:15 +00:00
|
|
|
global_default_config.rw = BDEVPERF_CONFIG_UNDEFINED;
|
2020-06-26 08:29:03 +00:00
|
|
|
config_set_cli_args(&global_default_config);
|
|
|
|
|
2020-07-09 20:33:15 +00:00
|
|
|
if ((int)global_default_config.rw == BDEVPERF_CONFIG_ERROR) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-06-26 08:29:03 +00:00
|
|
|
/* There is only a single instance of global job_config
|
|
|
|
* We just reset its value when we encounter new [global] section */
|
|
|
|
global_config = global_default_config;
|
|
|
|
|
|
|
|
for (s = spdk_conf_first_section(g_bdevperf_conf);
|
|
|
|
s != NULL;
|
|
|
|
s = spdk_conf_next_section(s)) {
|
|
|
|
config = calloc(1, sizeof(*config));
|
|
|
|
if (config == NULL) {
|
|
|
|
fprintf(stderr, "Unable to allocate memory for job config\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
config->name = spdk_conf_section_get_name(s);
|
|
|
|
is_global = strcmp(config->name, "global") == 0;
|
|
|
|
|
|
|
|
if (is_global) {
|
|
|
|
global_config = global_default_config;
|
|
|
|
}
|
|
|
|
|
|
|
|
config->filename = spdk_conf_section_get_val(s, "filename");
|
|
|
|
if (config->filename == NULL) {
|
|
|
|
config->filename = global_config.filename;
|
|
|
|
}
|
2020-07-21 16:23:28 +00:00
|
|
|
if (!is_global) {
|
|
|
|
if (config->filename == NULL) {
|
|
|
|
fprintf(stderr, "Job '%s' expects 'filename' parameter\n", config->name);
|
|
|
|
goto error;
|
|
|
|
} else if (strnlen(config->filename, BDEVPERF_CONFIG_MAX_FILENAME)
|
|
|
|
>= BDEVPERF_CONFIG_MAX_FILENAME) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"filename for '%s' job is too long. Max length is %d\n",
|
|
|
|
config->name, BDEVPERF_CONFIG_MAX_FILENAME);
|
|
|
|
goto error;
|
|
|
|
}
|
2020-06-26 08:29:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cpumask = spdk_conf_section_get_val(s, "cpumask");
|
|
|
|
if (cpumask == NULL) {
|
|
|
|
config->cpumask = global_config.cpumask;
|
|
|
|
} else if (spdk_cpuset_parse(&config->cpumask, cpumask)) {
|
|
|
|
fprintf(stderr, "Job '%s' has bad 'cpumask' value\n", config->name);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
config->bs = parse_uint_option(s, "bs", global_config.bs);
|
|
|
|
if (config->bs == BDEVPERF_CONFIG_ERROR) {
|
|
|
|
goto error;
|
|
|
|
} else if (config->bs == 0) {
|
|
|
|
fprintf(stderr, "'bs' of job '%s' must be greater than 0\n", config->name);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
config->iodepth = parse_uint_option(s, "iodepth", global_config.iodepth);
|
|
|
|
if (config->iodepth == BDEVPERF_CONFIG_ERROR) {
|
|
|
|
goto error;
|
|
|
|
} else if (config->iodepth == 0) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"'iodepth' of job '%s' must be greater than 0\n",
|
|
|
|
config->name);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
config->rwmixread = parse_uint_option(s, "rwmixread", global_config.rwmixread);
|
|
|
|
if (config->rwmixread == BDEVPERF_CONFIG_ERROR) {
|
|
|
|
goto error;
|
|
|
|
} else if (config->rwmixread > 100) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"'rwmixread' value of '%s' job is not in 0-100 range\n",
|
|
|
|
config->name);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2020-07-23 19:03:36 +00:00
|
|
|
config->offset = parse_uint_option(s, "offset", global_config.offset);
|
|
|
|
if (config->offset == BDEVPERF_CONFIG_ERROR) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2021-08-19 10:02:06 +00:00
|
|
|
val = parse_uint_option(s, "length", global_config.length);
|
|
|
|
if (val == BDEVPERF_CONFIG_ERROR) {
|
2020-07-23 19:03:36 +00:00
|
|
|
goto error;
|
|
|
|
}
|
2021-08-19 10:02:06 +00:00
|
|
|
config->length = val;
|
2020-07-23 19:03:36 +00:00
|
|
|
|
2020-07-09 20:33:15 +00:00
|
|
|
rw = spdk_conf_section_get_val(s, "rw");
|
|
|
|
config->rw = parse_rw(rw, global_config.rw);
|
|
|
|
if ((int)config->rw == BDEVPERF_CONFIG_ERROR) {
|
|
|
|
fprintf(stderr, "Job '%s' has bad 'rw' value\n", config->name);
|
|
|
|
goto error;
|
|
|
|
} else if (!is_global && (int)config->rw == BDEVPERF_CONFIG_UNDEFINED) {
|
|
|
|
fprintf(stderr, "Job '%s' has no 'rw' assigned\n", config->name);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2020-06-26 08:29:03 +00:00
|
|
|
if (is_global) {
|
|
|
|
config_set_cli_args(config);
|
|
|
|
global_config = *config;
|
|
|
|
free(config);
|
|
|
|
} else {
|
|
|
|
TAILQ_INSERT_TAIL(&job_config_list, config, link);
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Using job config with %d jobs\n", n);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
|
|
free(config);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-01-06 05:54:30 +00:00
|
|
|
static void
|
2020-03-19 21:27:32 +00:00
|
|
|
bdevperf_run(void *arg1)
|
2020-01-06 05:54:30 +00:00
|
|
|
{
|
2020-06-26 08:29:03 +00:00
|
|
|
uint32_t i;
|
|
|
|
|
2020-11-30 20:32:45 +00:00
|
|
|
g_main_thread = spdk_get_thread();
|
2019-07-03 11:53:17 +00:00
|
|
|
|
2020-06-26 08:29:03 +00:00
|
|
|
spdk_cpuset_zero(&g_all_cpuset);
|
|
|
|
SPDK_ENV_FOREACH_CORE(i) {
|
|
|
|
spdk_cpuset_set_cpu(&g_all_cpuset, i, true);
|
|
|
|
}
|
|
|
|
|
2019-07-03 13:56:14 +00:00
|
|
|
if (g_wait_for_tests) {
|
|
|
|
/* Do not perform any tests until RPC is received */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-10-13 20:50:35 +00:00
|
|
|
bdevperf_construct_job_configs();
|
2016-07-20 18:16:23 +00:00
|
|
|
}
|
|
|
|
|
2022-12-15 08:34:36 +00:00
|
|
|
static void
|
|
|
|
rpc_perform_tests_reset(void)
|
|
|
|
{
|
|
|
|
/* Reset g_run_rc to 0 for the next test run. */
|
|
|
|
g_run_rc = 0;
|
|
|
|
|
|
|
|
/* Reset g_stats to 0 for the next test run. */
|
|
|
|
memset(&g_stats, 0, sizeof(g_stats));
|
|
|
|
|
|
|
|
/* Reset g_show_performance_period_num to 0 for the next test run. */
|
|
|
|
g_show_performance_period_num = 0;
|
|
|
|
}
|
|
|
|
|
2020-01-30 02:25:14 +00:00
|
|
|
static void
|
|
|
|
rpc_perform_tests_cb(void)
|
|
|
|
{
|
|
|
|
struct spdk_json_write_ctx *w;
|
|
|
|
struct spdk_jsonrpc_request *request = g_request;
|
|
|
|
|
|
|
|
g_request = NULL;
|
|
|
|
|
|
|
|
if (g_run_rc == 0) {
|
|
|
|
w = spdk_jsonrpc_begin_result(request);
|
|
|
|
spdk_json_write_uint32(w, g_run_rc);
|
|
|
|
spdk_jsonrpc_end_result(request, w);
|
|
|
|
} else {
|
|
|
|
spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
|
|
|
"bdevperf failed with error %s", spdk_strerror(-g_run_rc));
|
|
|
|
}
|
|
|
|
|
2022-12-15 08:34:36 +00:00
|
|
|
rpc_perform_tests_reset();
|
2020-01-30 02:25:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
rpc_perform_tests(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
|
|
|
|
{
|
|
|
|
if (params != NULL) {
|
|
|
|
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
|
|
|
|
"perform_tests method requires no parameters");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (g_request != NULL) {
|
|
|
|
fprintf(stderr, "Another test is already in progress.\n");
|
|
|
|
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
|
|
|
spdk_strerror(-EINPROGRESS));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
g_request = request;
|
|
|
|
|
2021-10-11 21:12:32 +00:00
|
|
|
/* Only construct job configs at the first test run. */
|
|
|
|
if (TAILQ_EMPTY(&job_config_list)) {
|
|
|
|
bdevperf_construct_job_configs();
|
|
|
|
} else {
|
|
|
|
bdevperf_construct_jobs();
|
|
|
|
}
|
2020-01-30 02:25:14 +00:00
|
|
|
}
|
|
|
|
SPDK_RPC_REGISTER("perform_tests", rpc_perform_tests, SPDK_RPC_RUNTIME)
|
|
|
|
|
2020-02-05 08:06:23 +00:00
|
|
|
static void
|
2020-03-19 21:27:32 +00:00
|
|
|
_bdevperf_job_drain(void *ctx)
|
2020-02-05 08:06:23 +00:00
|
|
|
{
|
2020-03-19 21:27:32 +00:00
|
|
|
bdevperf_job_drain(ctx);
|
2020-02-05 08:06:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
spdk_bdevperf_shutdown_cb(void)
|
|
|
|
{
|
|
|
|
g_shutdown = true;
|
2020-03-19 21:27:32 +00:00
|
|
|
struct bdevperf_job *job, *tmp;
|
2020-02-05 08:06:23 +00:00
|
|
|
|
2020-03-19 23:05:17 +00:00
|
|
|
if (g_bdevperf.running_jobs == 0) {
|
2020-03-19 22:26:05 +00:00
|
|
|
bdevperf_test_done(NULL);
|
2020-02-05 08:06:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-19 21:27:32 +00:00
|
|
|
/* Iterate jobs to stop all I/O */
|
|
|
|
TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, tmp) {
|
|
|
|
spdk_thread_send_msg(job->thread, _bdevperf_job_drain, job);
|
|
|
|
}
|
2020-02-05 08:06:23 +00:00
|
|
|
}
|
|
|
|
|
2019-01-08 19:40:06 +00:00
|
|
|
static int
|
2018-08-09 11:54:39 +00:00
|
|
|
bdevperf_parse_arg(int ch, char *arg)
|
|
|
|
{
|
2019-01-17 14:05:53 +00:00
|
|
|
long long tmp;
|
|
|
|
|
|
|
|
if (ch == 'w') {
|
2018-08-09 11:54:39 +00:00
|
|
|
g_workload_type = optarg;
|
2019-06-04 08:51:36 +00:00
|
|
|
} else if (ch == 'T') {
|
2020-03-18 22:21:37 +00:00
|
|
|
g_job_bdev_name = optarg;
|
2019-07-03 13:56:14 +00:00
|
|
|
} else if (ch == 'z') {
|
|
|
|
g_wait_for_tests = true;
|
2020-09-18 19:41:57 +00:00
|
|
|
} else if (ch == 'Z') {
|
|
|
|
g_zcopy = true;
|
2020-12-18 13:05:37 +00:00
|
|
|
} else if (ch == 'X') {
|
2020-05-26 22:05:37 +00:00
|
|
|
g_abort = true;
|
2019-10-09 19:10:17 +00:00
|
|
|
} else if (ch == 'C') {
|
2020-03-19 23:00:58 +00:00
|
|
|
g_multithread_mode = true;
|
2019-11-08 23:30:48 +00:00
|
|
|
} else if (ch == 'f') {
|
|
|
|
g_continue_on_failure = true;
|
2020-06-26 08:29:03 +00:00
|
|
|
} else if (ch == 'j') {
|
|
|
|
g_bdevperf_conf_file = optarg;
|
2021-05-06 23:12:43 +00:00
|
|
|
} else if (ch == 'F') {
|
|
|
|
char *endptr;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
g_zipf_theta = strtod(optarg, &endptr);
|
|
|
|
if (errno || optarg == endptr || g_zipf_theta < 0) {
|
|
|
|
fprintf(stderr, "Illegal zipf theta value %s\n", optarg);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2022-09-13 10:07:09 +00:00
|
|
|
} else if (ch == 'l') {
|
|
|
|
g_latency_display_level++;
|
2023-02-20 10:54:01 +00:00
|
|
|
} else if (ch == 'D') {
|
|
|
|
g_random_map = true;
|
2023-03-01 07:45:37 +00:00
|
|
|
} else if (ch == 'E') {
|
|
|
|
g_one_thread_per_lcore = true;
|
2019-01-17 14:05:53 +00:00
|
|
|
} else {
|
2019-01-22 23:27:37 +00: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 14:05:53 +00: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;
|
2020-05-27 00:25:56 +00:00
|
|
|
case 'k':
|
|
|
|
g_timeout_in_sec = tmp;
|
|
|
|
break;
|
2019-01-17 14:05:53 +00:00
|
|
|
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;
|
2022-12-06 06:16:33 +00:00
|
|
|
g_show_performance_period_in_usec = tmp * SPDK_SEC_TO_USEC;
|
2019-01-17 14:05:53 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2018-08-09 11:54:39 +00:00
|
|
|
}
|
2019-01-08 19:40:06 +00:00
|
|
|
return 0;
|
2018-08-09 11:54:39 +00:00
|
|
|
}
|
|
|
|
|
2019-07-03 13:56:14 +00:00
|
|
|
static void
|
2020-01-30 02:25:14 +00:00
|
|
|
bdevperf_usage(void)
|
2019-07-03 13:56:14 +00:00
|
|
|
{
|
2020-01-30 02:25:14 +00: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");
|
2020-05-27 00:25:56 +00:00
|
|
|
printf(" -k <timeout> timeout in seconds to detect starved I/O (default is 0 and disabled)\n");
|
2020-01-30 02:25:14 +00:00
|
|
|
printf(" -M <percent> rwmixread (100 for reads, 0 for writes)\n");
|
|
|
|
printf(" -P <num> number of moving average period\n");
|
|
|
|
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");
|
|
|
|
printf(" -S <period> show performance result in real time every <period> seconds\n");
|
2020-03-19 21:48:23 +00:00
|
|
|
printf(" -T <bdev> bdev to run against. Default: all available bdevs.\n");
|
2020-01-30 02:25:14 +00:00
|
|
|
printf(" -f continue processing I/O even after failures\n");
|
2021-05-06 23:12:43 +00:00
|
|
|
printf(" -F <zipf theta> use zipf distribution for random I/O\n");
|
2020-09-18 19:41:57 +00:00
|
|
|
printf(" -Z enable using zcopy bdev API for read or write I/O\n");
|
2020-01-30 02:25:14 +00:00
|
|
|
printf(" -z start bdevperf, but wait for RPC to start tests\n");
|
2020-12-18 13:05:37 +00:00
|
|
|
printf(" -X abort timed out I/O\n");
|
2020-01-30 02:25:14 +00:00
|
|
|
printf(" -C enable every core to send I/Os to each bdev\n");
|
2021-02-08 21:30:03 +00:00
|
|
|
printf(" -j <filename> use job config file\n");
|
2022-09-13 10:07:09 +00:00
|
|
|
printf(" -l display latency histogram, default: disable. -l display summary, -ll display details\n");
|
2023-02-20 10:54:01 +00:00
|
|
|
printf(" -D use a random map for picking offsets not previously read or written (for all jobs)\n");
|
2023-03-01 07:45:37 +00:00
|
|
|
printf(" -E share per lcore thread among jobs. Available only if -j is not used.\n");
|
2020-01-30 02:25:14 +00:00
|
|
|
}
|
2019-07-03 13:56:14 +00:00
|
|
|
|
2020-01-30 02:25:14 +00:00
|
|
|
static int
|
|
|
|
verify_test_params(struct spdk_app_opts *opts)
|
|
|
|
{
|
|
|
|
/* When RPC is used for starting tests and
|
|
|
|
* no rpc_addr was configured for the app,
|
|
|
|
* use the default address. */
|
|
|
|
if (g_wait_for_tests && opts->rpc_addr == NULL) {
|
|
|
|
opts->rpc_addr = SPDK_DEFAULT_RPC_ADDR;
|
|
|
|
}
|
2019-07-03 13:56:14 +00:00
|
|
|
|
2020-06-26 08:29:03 +00:00
|
|
|
if (!g_bdevperf_conf_file && g_queue_depth <= 0) {
|
2020-11-26 13:58:50 +00:00
|
|
|
goto out;
|
2019-07-03 13:56:14 +00:00
|
|
|
}
|
2020-06-26 08:29:03 +00:00
|
|
|
if (!g_bdevperf_conf_file && g_io_size <= 0) {
|
2020-11-26 13:58:50 +00:00
|
|
|
goto out;
|
2020-01-30 02:25:14 +00:00
|
|
|
}
|
2020-07-09 20:33:15 +00:00
|
|
|
if (!g_bdevperf_conf_file && !g_workload_type) {
|
2020-11-26 13:58:50 +00:00
|
|
|
goto out;
|
2020-01-30 02:25:14 +00:00
|
|
|
}
|
2023-03-01 07:45:37 +00:00
|
|
|
if (g_bdevperf_conf_file && g_one_thread_per_lcore) {
|
|
|
|
printf("If bdevperf's config file is used, per lcore thread cannot be used\n");
|
|
|
|
goto out;
|
|
|
|
}
|
2020-01-30 02:25:14 +00:00
|
|
|
if (g_time_in_sec <= 0) {
|
2020-11-26 13:58:50 +00:00
|
|
|
goto out;
|
2020-01-30 02:25:14 +00:00
|
|
|
}
|
2022-12-06 06:16:33 +00:00
|
|
|
g_time_in_usec = g_time_in_sec * SPDK_SEC_TO_USEC;
|
2020-01-10 01:40:25 +00:00
|
|
|
|
2020-05-27 00:25:56 +00:00
|
|
|
if (g_timeout_in_sec < 0) {
|
2020-11-26 13:58:50 +00:00
|
|
|
goto out;
|
2020-05-27 00:25:56 +00:00
|
|
|
}
|
|
|
|
|
2022-09-09 07:20:55 +00:00
|
|
|
if (g_abort && !g_timeout_in_sec) {
|
|
|
|
printf("Timeout must be set for abort option, Ignoring g_abort\n");
|
|
|
|
}
|
|
|
|
|
2020-01-30 02:25:14 +00: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");
|
|
|
|
return 1;
|
|
|
|
}
|
2019-07-03 13:56:14 +00:00
|
|
|
|
2020-07-09 20:33:15 +00:00
|
|
|
if (g_io_size > SPDK_BDEV_LARGE_BUF_MAX_SIZE) {
|
|
|
|
printf("I/O size of %d is greater than zero copy threshold (%d).\n",
|
|
|
|
g_io_size, SPDK_BDEV_LARGE_BUF_MAX_SIZE);
|
|
|
|
printf("Zero copy mechanism will not be used.\n");
|
|
|
|
g_zcopy = false;
|
2020-01-30 02:25:14 +00:00
|
|
|
}
|
|
|
|
|
2020-07-09 20:33:15 +00:00
|
|
|
if (g_bdevperf_conf_file) {
|
|
|
|
/* workload_type verification happens during config file parsing */
|
|
|
|
return 0;
|
2019-07-03 13:56:14 +00:00
|
|
|
}
|
2020-01-30 02:25:14 +00:00
|
|
|
|
|
|
|
if (!strcmp(g_workload_type, "verify") ||
|
|
|
|
!strcmp(g_workload_type, "reset")) {
|
|
|
|
g_rw_percentage = 50;
|
|
|
|
if (g_io_size > SPDK_BDEV_LARGE_BUF_MAX_SIZE) {
|
|
|
|
fprintf(stderr, "Unable to exceed max I/O size of %d for verify. (%d provided).\n",
|
|
|
|
SPDK_BDEV_LARGE_BUF_MAX_SIZE, g_io_size);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
g_verify = true;
|
|
|
|
if (!strcmp(g_workload_type, "reset")) {
|
|
|
|
g_reset = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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") ||
|
|
|
|
!strcmp(g_workload_type, "write_zeroes") ||
|
|
|
|
!strcmp(g_workload_type, "flush")) {
|
|
|
|
if (g_mix_specified) {
|
|
|
|
fprintf(stderr, "Ignoring -M option... Please use -M option"
|
|
|
|
" only when using rw or randrw.\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-09 20:33:15 +00:00
|
|
|
if (!strcmp(g_workload_type, "rw") ||
|
|
|
|
!strcmp(g_workload_type, "randrw")) {
|
2020-01-30 02:25:14 +00: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");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-20 10:54:01 +00:00
|
|
|
if (strcmp(g_workload_type, "randread") &&
|
|
|
|
strcmp(g_workload_type, "randwrite") &&
|
|
|
|
strcmp(g_workload_type, "randrw")) {
|
|
|
|
if (g_random_map) {
|
|
|
|
fprintf(stderr, "Ignoring -D option... Please use -D option"
|
|
|
|
" only when using randread, randwrite or randrw.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-30 02:25:14 +00:00
|
|
|
return 0;
|
2020-11-26 13:58:50 +00:00
|
|
|
out:
|
|
|
|
spdk_app_usage();
|
|
|
|
bdevperf_usage();
|
|
|
|
return 1;
|
2019-07-03 13:56:14 +00:00
|
|
|
}
|
|
|
|
|
2016-07-20 18:16:23 +00:00
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
2017-06-06 06:31:47 +00:00
|
|
|
struct spdk_app_opts opts = {};
|
2018-02-22 23:50:51 +00:00
|
|
|
int rc;
|
2018-08-09 11:54:39 +00:00
|
|
|
|
2022-10-21 13:24:55 +00:00
|
|
|
/* Use the runtime PID to set the random seed */
|
|
|
|
srand(getpid());
|
|
|
|
|
2020-11-30 11:38:37 +00:00
|
|
|
spdk_app_opts_init(&opts, sizeof(opts));
|
2018-10-18 22:27:58 +00:00
|
|
|
opts.name = "bdevperf";
|
2018-08-09 11:54:39 +00:00
|
|
|
opts.rpc_addr = NULL;
|
|
|
|
opts.shutdown_cb = spdk_bdevperf_shutdown_cb;
|
2016-07-20 18:16:23 +00:00
|
|
|
|
2023-03-01 07:45:37 +00:00
|
|
|
if ((rc = spdk_app_parse_args(argc, argv, &opts, "Zzfq:o:t:w:k:CEF:M:P:S:T:Xlj:D", NULL,
|
2018-08-09 11:54:39 +00:00
|
|
|
bdevperf_parse_arg, bdevperf_usage)) !=
|
|
|
|
SPDK_APP_PARSE_ARGS_SUCCESS) {
|
|
|
|
return rc;
|
2016-07-20 18:16:23 +00:00
|
|
|
}
|
|
|
|
|
2020-06-26 08:29:03 +00:00
|
|
|
if (read_job_config()) {
|
|
|
|
free_job_config();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-06-07 09:08:39 +00:00
|
|
|
if (verify_test_params(&opts) != 0) {
|
2020-06-26 08:29:03 +00:00
|
|
|
free_job_config();
|
2016-07-20 18:16:23 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2019-02-28 21:42:07 +00:00
|
|
|
rc = spdk_app_start(&opts, bdevperf_run, NULL);
|
2016-07-20 18:16:23 +00:00
|
|
|
|
|
|
|
spdk_app_fini();
|
2020-06-26 08:29:03 +00:00
|
|
|
free_job_config();
|
2019-12-26 00:50:50 +00:00
|
|
|
return rc;
|
2016-07-20 18:16:23 +00:00
|
|
|
}
|