129784f35SKrzysztof Karas /* SPDX-License-Identifier: BSD-3-Clause 229784f35SKrzysztof Karas * Copyright (C) 2016 Intel Corporation. 39ef9d4bdSShuhei Matsumoto * Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. 429784f35SKrzysztof Karas * All rights reserved. 529784f35SKrzysztof Karas */ 629784f35SKrzysztof Karas 729784f35SKrzysztof Karas #include "spdk/stdinc.h" 829784f35SKrzysztof Karas 929784f35SKrzysztof Karas #include "spdk/bdev.h" 1029784f35SKrzysztof Karas #include "spdk/accel.h" 1129784f35SKrzysztof Karas #include "spdk/endian.h" 1229784f35SKrzysztof Karas #include "spdk/env.h" 1329784f35SKrzysztof Karas #include "spdk/event.h" 1429784f35SKrzysztof Karas #include "spdk/log.h" 1529784f35SKrzysztof Karas #include "spdk/util.h" 1629784f35SKrzysztof Karas #include "spdk/thread.h" 1729784f35SKrzysztof Karas #include "spdk/string.h" 1829784f35SKrzysztof Karas #include "spdk/rpc.h" 1929784f35SKrzysztof Karas #include "spdk/bit_array.h" 2029784f35SKrzysztof Karas #include "spdk/conf.h" 2129784f35SKrzysztof Karas #include "spdk/zipf.h" 22723dd06eSRichael Zhuang #include "spdk/histogram_data.h" 2329784f35SKrzysztof Karas 2429784f35SKrzysztof Karas #define BDEVPERF_CONFIG_MAX_FILENAME 1024 2529784f35SKrzysztof Karas #define BDEVPERF_CONFIG_UNDEFINED -1 2629784f35SKrzysztof Karas #define BDEVPERF_CONFIG_ERROR -2 2729784f35SKrzysztof Karas 2829784f35SKrzysztof Karas struct bdevperf_task { 2929784f35SKrzysztof Karas struct iovec iov; 3029784f35SKrzysztof Karas struct bdevperf_job *job; 3129784f35SKrzysztof Karas struct spdk_bdev_io *bdev_io; 3229784f35SKrzysztof Karas void *buf; 3329784f35SKrzysztof Karas void *md_buf; 3429784f35SKrzysztof Karas uint64_t offset_blocks; 3529784f35SKrzysztof Karas struct bdevperf_task *task_to_abort; 3629784f35SKrzysztof Karas enum spdk_bdev_io_type io_type; 3729784f35SKrzysztof Karas TAILQ_ENTRY(bdevperf_task) link; 3829784f35SKrzysztof Karas struct spdk_bdev_io_wait_entry bdev_io_wait; 3929784f35SKrzysztof Karas }; 4029784f35SKrzysztof Karas 4129784f35SKrzysztof Karas static const char *g_workload_type = NULL; 4229784f35SKrzysztof Karas static int g_io_size = 0; 4329784f35SKrzysztof Karas /* initialize to invalid value so we can detect if user overrides it. */ 4429784f35SKrzysztof Karas static int g_rw_percentage = -1; 4529784f35SKrzysztof Karas static bool g_verify = false; 4629784f35SKrzysztof Karas static bool g_reset = false; 4729784f35SKrzysztof Karas static bool g_continue_on_failure = false; 4829784f35SKrzysztof Karas static bool g_abort = false; 4929784f35SKrzysztof Karas static bool g_error_to_exit = false; 5029784f35SKrzysztof Karas static int g_queue_depth = 0; 5129784f35SKrzysztof Karas static uint64_t g_time_in_usec; 5229784f35SKrzysztof Karas static int g_show_performance_real_time = 0; 53045c781dSRichael Zhuang static uint64_t g_show_performance_period_in_usec = SPDK_SEC_TO_USEC; 5429784f35SKrzysztof Karas static uint64_t g_show_performance_period_num = 0; 5529784f35SKrzysztof Karas static uint64_t g_show_performance_ema_period = 0; 5629784f35SKrzysztof Karas static int g_run_rc = 0; 5729784f35SKrzysztof Karas static bool g_shutdown = false; 5829784f35SKrzysztof Karas static uint64_t g_start_tsc; 5929784f35SKrzysztof Karas static uint64_t g_shutdown_tsc; 6029784f35SKrzysztof Karas static bool g_zcopy = false; 6129784f35SKrzysztof Karas static struct spdk_thread *g_main_thread; 6229784f35SKrzysztof Karas static int g_time_in_sec = 0; 6329784f35SKrzysztof Karas static bool g_mix_specified = false; 6429784f35SKrzysztof Karas static const char *g_job_bdev_name; 6529784f35SKrzysztof Karas static bool g_wait_for_tests = false; 6629784f35SKrzysztof Karas static struct spdk_jsonrpc_request *g_request = NULL; 6729784f35SKrzysztof Karas static bool g_multithread_mode = false; 6829784f35SKrzysztof Karas static int g_timeout_in_sec; 6929784f35SKrzysztof Karas static struct spdk_conf *g_bdevperf_conf = NULL; 7029784f35SKrzysztof Karas static const char *g_bdevperf_conf_file = NULL; 7129784f35SKrzysztof Karas static double g_zipf_theta; 72d254a3b9SSlawomir Ptak static bool g_random_map = false; 7329784f35SKrzysztof Karas 7429784f35SKrzysztof Karas static struct spdk_cpuset g_all_cpuset; 7529784f35SKrzysztof Karas static struct spdk_poller *g_perf_timer = NULL; 7629784f35SKrzysztof Karas 7729784f35SKrzysztof Karas static void bdevperf_submit_single(struct bdevperf_job *job, struct bdevperf_task *task); 7829784f35SKrzysztof Karas static void rpc_perform_tests_cb(void); 7929784f35SKrzysztof Karas 80723dd06eSRichael Zhuang static uint32_t g_bdev_count = 0; 81723dd06eSRichael Zhuang static uint32_t g_latency_display_level; 82723dd06eSRichael Zhuang 838e373044SShuhei Matsumoto static bool g_one_thread_per_lcore = false; 848e373044SShuhei Matsumoto 85723dd06eSRichael Zhuang static const double g_latency_cutoffs[] = { 86723dd06eSRichael Zhuang 0.01, 87723dd06eSRichael Zhuang 0.10, 88723dd06eSRichael Zhuang 0.25, 89723dd06eSRichael Zhuang 0.50, 90723dd06eSRichael Zhuang 0.75, 91723dd06eSRichael Zhuang 0.90, 92723dd06eSRichael Zhuang 0.95, 93723dd06eSRichael Zhuang 0.98, 94723dd06eSRichael Zhuang 0.99, 95723dd06eSRichael Zhuang 0.995, 96723dd06eSRichael Zhuang 0.999, 97723dd06eSRichael Zhuang 0.9999, 98723dd06eSRichael Zhuang 0.99999, 99723dd06eSRichael Zhuang 0.999999, 100723dd06eSRichael Zhuang 0.9999999, 101723dd06eSRichael Zhuang -1, 102723dd06eSRichael Zhuang }; 103723dd06eSRichael Zhuang 104723dd06eSRichael Zhuang struct latency_info { 105723dd06eSRichael Zhuang uint64_t min; 106723dd06eSRichael Zhuang uint64_t max; 107723dd06eSRichael Zhuang uint64_t total; 108723dd06eSRichael Zhuang }; 109723dd06eSRichael Zhuang 11029784f35SKrzysztof Karas struct bdevperf_job { 11129784f35SKrzysztof Karas char *name; 11229784f35SKrzysztof Karas struct spdk_bdev *bdev; 11329784f35SKrzysztof Karas struct spdk_bdev_desc *bdev_desc; 11429784f35SKrzysztof Karas struct spdk_io_channel *ch; 11529784f35SKrzysztof Karas TAILQ_ENTRY(bdevperf_job) link; 11629784f35SKrzysztof Karas struct spdk_thread *thread; 11729784f35SKrzysztof Karas 11829784f35SKrzysztof Karas const char *workload_type; 11929784f35SKrzysztof Karas int io_size; 12029784f35SKrzysztof Karas int rw_percentage; 12129784f35SKrzysztof Karas bool is_random; 12229784f35SKrzysztof Karas bool verify; 12329784f35SKrzysztof Karas bool reset; 12429784f35SKrzysztof Karas bool continue_on_failure; 12529784f35SKrzysztof Karas bool unmap; 12629784f35SKrzysztof Karas bool write_zeroes; 12729784f35SKrzysztof Karas bool flush; 12829784f35SKrzysztof Karas bool abort; 12929784f35SKrzysztof Karas int queue_depth; 13029784f35SKrzysztof Karas unsigned int seed; 13129784f35SKrzysztof Karas 13229784f35SKrzysztof Karas uint64_t io_completed; 13329784f35SKrzysztof Karas uint64_t io_failed; 13429784f35SKrzysztof Karas uint64_t io_timeout; 13529784f35SKrzysztof Karas uint64_t prev_io_completed; 13629784f35SKrzysztof Karas double ema_io_per_second; 13729784f35SKrzysztof Karas int current_queue_depth; 13829784f35SKrzysztof Karas uint64_t size_in_ios; 13929784f35SKrzysztof Karas uint64_t ios_base; 14029784f35SKrzysztof Karas uint64_t offset_in_ios; 14129784f35SKrzysztof Karas uint64_t io_size_blocks; 14229784f35SKrzysztof Karas uint64_t buf_size; 14329784f35SKrzysztof Karas uint32_t dif_check_flags; 14429784f35SKrzysztof Karas bool is_draining; 14529784f35SKrzysztof Karas struct spdk_poller *run_timer; 14629784f35SKrzysztof Karas struct spdk_poller *reset_timer; 14729784f35SKrzysztof Karas struct spdk_bit_array *outstanding; 14829784f35SKrzysztof Karas struct spdk_zipf *zipf; 14929784f35SKrzysztof Karas TAILQ_HEAD(, bdevperf_task) task_list; 15029784f35SKrzysztof Karas uint64_t run_time_in_usec; 151723dd06eSRichael Zhuang 152723dd06eSRichael Zhuang /* keep channel's histogram data before being destroyed */ 153723dd06eSRichael Zhuang struct spdk_histogram_data *histogram; 154d254a3b9SSlawomir Ptak struct spdk_bit_array *random_map; 15529784f35SKrzysztof Karas }; 15629784f35SKrzysztof Karas 15729784f35SKrzysztof Karas struct spdk_bdevperf { 15829784f35SKrzysztof Karas TAILQ_HEAD(, bdevperf_job) jobs; 15929784f35SKrzysztof Karas uint32_t running_jobs; 16029784f35SKrzysztof Karas }; 16129784f35SKrzysztof Karas 16229784f35SKrzysztof Karas static struct spdk_bdevperf g_bdevperf = { 16329784f35SKrzysztof Karas .jobs = TAILQ_HEAD_INITIALIZER(g_bdevperf.jobs), 16429784f35SKrzysztof Karas .running_jobs = 0, 16529784f35SKrzysztof Karas }; 16629784f35SKrzysztof Karas 16729784f35SKrzysztof Karas enum job_config_rw { 16829784f35SKrzysztof Karas JOB_CONFIG_RW_READ = 0, 16929784f35SKrzysztof Karas JOB_CONFIG_RW_WRITE, 17029784f35SKrzysztof Karas JOB_CONFIG_RW_RANDREAD, 17129784f35SKrzysztof Karas JOB_CONFIG_RW_RANDWRITE, 17229784f35SKrzysztof Karas JOB_CONFIG_RW_RW, 17329784f35SKrzysztof Karas JOB_CONFIG_RW_RANDRW, 17429784f35SKrzysztof Karas JOB_CONFIG_RW_VERIFY, 17529784f35SKrzysztof Karas JOB_CONFIG_RW_RESET, 17629784f35SKrzysztof Karas JOB_CONFIG_RW_UNMAP, 17729784f35SKrzysztof Karas JOB_CONFIG_RW_FLUSH, 17829784f35SKrzysztof Karas JOB_CONFIG_RW_WRITE_ZEROES, 17929784f35SKrzysztof Karas }; 18029784f35SKrzysztof Karas 18129784f35SKrzysztof Karas /* Storing values from a section of job config file */ 18229784f35SKrzysztof Karas struct job_config { 18329784f35SKrzysztof Karas const char *name; 18429784f35SKrzysztof Karas const char *filename; 18529784f35SKrzysztof Karas struct spdk_cpuset cpumask; 18629784f35SKrzysztof Karas int bs; 18729784f35SKrzysztof Karas int iodepth; 18829784f35SKrzysztof Karas int rwmixread; 1898e373044SShuhei Matsumoto uint32_t lcore; 19029784f35SKrzysztof Karas int64_t offset; 19129784f35SKrzysztof Karas uint64_t length; 19229784f35SKrzysztof Karas enum job_config_rw rw; 19329784f35SKrzysztof Karas TAILQ_ENTRY(job_config) link; 19429784f35SKrzysztof Karas }; 19529784f35SKrzysztof Karas 19629784f35SKrzysztof Karas TAILQ_HEAD(, job_config) job_config_list 19729784f35SKrzysztof Karas = TAILQ_HEAD_INITIALIZER(job_config_list); 19829784f35SKrzysztof Karas 19929784f35SKrzysztof Karas static bool g_performance_dump_active = false; 20029784f35SKrzysztof Karas 20129784f35SKrzysztof Karas struct bdevperf_aggregate_stats { 20229784f35SKrzysztof Karas struct bdevperf_job *current_job; 20329784f35SKrzysztof Karas uint64_t io_time_in_usec; 20429784f35SKrzysztof Karas uint64_t ema_period; 20529784f35SKrzysztof Karas double total_io_per_second; 20629784f35SKrzysztof Karas double total_mb_per_second; 20729784f35SKrzysztof Karas double total_failed_per_second; 20829784f35SKrzysztof Karas double total_timeout_per_second; 209723dd06eSRichael Zhuang double min_latency; 210723dd06eSRichael Zhuang double max_latency; 211723dd06eSRichael Zhuang uint64_t total_io_completed; 212723dd06eSRichael Zhuang uint64_t total_tsc; 21329784f35SKrzysztof Karas }; 21429784f35SKrzysztof Karas 215723dd06eSRichael Zhuang static struct bdevperf_aggregate_stats g_stats = {.min_latency = (double)UINT64_MAX}; 21629784f35SKrzysztof Karas 2178e373044SShuhei Matsumoto struct lcore_thread { 2188e373044SShuhei Matsumoto struct spdk_thread *thread; 2198e373044SShuhei Matsumoto uint32_t lcore; 2208e373044SShuhei Matsumoto TAILQ_ENTRY(lcore_thread) link; 2218e373044SShuhei Matsumoto }; 2228e373044SShuhei Matsumoto 2238e373044SShuhei Matsumoto TAILQ_HEAD(, lcore_thread) g_lcore_thread_list 2248e373044SShuhei Matsumoto = TAILQ_HEAD_INITIALIZER(g_lcore_thread_list); 2258e373044SShuhei Matsumoto 22629784f35SKrzysztof Karas /* 22729784f35SKrzysztof Karas * Cumulative Moving Average (CMA): average of all data up to current 22829784f35SKrzysztof Karas * Exponential Moving Average (EMA): weighted mean of the previous n data and more weight is given to recent 22929784f35SKrzysztof Karas * Simple Moving Average (SMA): unweighted mean of the previous n data 23029784f35SKrzysztof Karas * 23129784f35SKrzysztof Karas * Bdevperf supports CMA and EMA. 23229784f35SKrzysztof Karas */ 23329784f35SKrzysztof Karas static double 23429784f35SKrzysztof Karas get_cma_io_per_second(struct bdevperf_job *job, uint64_t io_time_in_usec) 23529784f35SKrzysztof Karas { 236045c781dSRichael Zhuang return (double)job->io_completed * SPDK_SEC_TO_USEC / io_time_in_usec; 23729784f35SKrzysztof Karas } 23829784f35SKrzysztof Karas 23929784f35SKrzysztof Karas static double 24029784f35SKrzysztof Karas get_ema_io_per_second(struct bdevperf_job *job, uint64_t ema_period) 24129784f35SKrzysztof Karas { 24229784f35SKrzysztof Karas double io_completed, io_per_second; 24329784f35SKrzysztof Karas 24429784f35SKrzysztof Karas io_completed = job->io_completed; 245045c781dSRichael Zhuang io_per_second = (double)(io_completed - job->prev_io_completed) * SPDK_SEC_TO_USEC 24629784f35SKrzysztof Karas / g_show_performance_period_in_usec; 24729784f35SKrzysztof Karas job->prev_io_completed = io_completed; 24829784f35SKrzysztof Karas 24929784f35SKrzysztof Karas job->ema_io_per_second += (io_per_second - job->ema_io_per_second) * 2 25029784f35SKrzysztof Karas / (ema_period + 1); 25129784f35SKrzysztof Karas return job->ema_io_per_second; 25229784f35SKrzysztof Karas } 25329784f35SKrzysztof Karas 25429784f35SKrzysztof Karas static void 255723dd06eSRichael Zhuang get_avg_latency(void *ctx, uint64_t start, uint64_t end, uint64_t count, 256723dd06eSRichael Zhuang uint64_t total, uint64_t so_far) 257723dd06eSRichael Zhuang { 258723dd06eSRichael Zhuang struct latency_info *latency_info = ctx; 259723dd06eSRichael Zhuang 260723dd06eSRichael Zhuang if (count == 0) { 261723dd06eSRichael Zhuang return; 262723dd06eSRichael Zhuang } 263723dd06eSRichael Zhuang 264723dd06eSRichael Zhuang latency_info->total += (start + end) / 2 * count; 265723dd06eSRichael Zhuang 266723dd06eSRichael Zhuang if (so_far == count) { 267723dd06eSRichael Zhuang latency_info->min = start; 268723dd06eSRichael Zhuang } 269723dd06eSRichael Zhuang 270723dd06eSRichael Zhuang if (so_far == total) { 271723dd06eSRichael Zhuang latency_info->max = end; 272723dd06eSRichael Zhuang } 273723dd06eSRichael Zhuang } 274723dd06eSRichael Zhuang 275723dd06eSRichael Zhuang static void 27629784f35SKrzysztof Karas performance_dump_job(struct bdevperf_aggregate_stats *stats, struct bdevperf_job *job) 27729784f35SKrzysztof Karas { 27829784f35SKrzysztof Karas double io_per_second, mb_per_second, failed_per_second, timeout_per_second; 279723dd06eSRichael Zhuang double average_latency = 0.0, min_latency, max_latency; 28029784f35SKrzysztof Karas uint64_t time_in_usec; 281723dd06eSRichael Zhuang uint64_t tsc_rate; 282723dd06eSRichael Zhuang uint64_t total_io; 283723dd06eSRichael Zhuang struct latency_info latency_info = {}; 28429784f35SKrzysztof Karas 28572da547aSShuhei Matsumoto printf("\r Job: %s (Core Mask 0x%s)\n", job->name, 28629784f35SKrzysztof Karas spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread))); 28729784f35SKrzysztof Karas 28829784f35SKrzysztof Karas if (job->io_failed > 0 && !job->reset && !job->continue_on_failure) { 28929784f35SKrzysztof Karas printf("\r Job: %s ended in about %.2f seconds with error\n", 29072da547aSShuhei Matsumoto job->name, (double)job->run_time_in_usec / SPDK_SEC_TO_USEC); 29129784f35SKrzysztof Karas } 29229784f35SKrzysztof Karas if (job->verify) { 29329784f35SKrzysztof Karas printf("\t Verification LBA range: start 0x%" PRIx64 " length 0x%" PRIx64 "\n", 29429784f35SKrzysztof Karas job->ios_base, job->size_in_ios); 29529784f35SKrzysztof Karas } 29629784f35SKrzysztof Karas 29729784f35SKrzysztof Karas if (g_performance_dump_active == true) { 29829784f35SKrzysztof Karas /* Use job's actual run time as Job has ended */ 29929784f35SKrzysztof Karas if (job->io_failed > 0 && !job->continue_on_failure) { 30029784f35SKrzysztof Karas time_in_usec = job->run_time_in_usec; 30129784f35SKrzysztof Karas } else { 30229784f35SKrzysztof Karas time_in_usec = stats->io_time_in_usec; 30329784f35SKrzysztof Karas } 30429784f35SKrzysztof Karas } else { 30529784f35SKrzysztof Karas time_in_usec = job->run_time_in_usec; 30629784f35SKrzysztof Karas } 30729784f35SKrzysztof Karas 30829784f35SKrzysztof Karas if (stats->ema_period == 0) { 30929784f35SKrzysztof Karas io_per_second = get_cma_io_per_second(job, time_in_usec); 31029784f35SKrzysztof Karas } else { 31129784f35SKrzysztof Karas io_per_second = get_ema_io_per_second(job, stats->ema_period); 31229784f35SKrzysztof Karas } 313723dd06eSRichael Zhuang 314723dd06eSRichael Zhuang tsc_rate = spdk_get_ticks_hz(); 31529784f35SKrzysztof Karas mb_per_second = io_per_second * job->io_size / (1024 * 1024); 31629784f35SKrzysztof Karas 317723dd06eSRichael Zhuang spdk_histogram_data_iterate(job->histogram, get_avg_latency, &latency_info); 318723dd06eSRichael Zhuang 319723dd06eSRichael Zhuang total_io = job->io_completed + job->io_failed; 320723dd06eSRichael Zhuang if (total_io != 0) { 321045c781dSRichael Zhuang average_latency = (double)latency_info.total / total_io * SPDK_SEC_TO_USEC / tsc_rate; 322723dd06eSRichael Zhuang } 323045c781dSRichael Zhuang min_latency = (double)latency_info.min * SPDK_SEC_TO_USEC / tsc_rate; 324045c781dSRichael Zhuang max_latency = (double)latency_info.max * SPDK_SEC_TO_USEC / tsc_rate; 325723dd06eSRichael Zhuang 326045c781dSRichael Zhuang failed_per_second = (double)job->io_failed * SPDK_SEC_TO_USEC / time_in_usec; 327045c781dSRichael Zhuang timeout_per_second = (double)job->io_timeout * SPDK_SEC_TO_USEC / time_in_usec; 32829784f35SKrzysztof Karas 32929784f35SKrzysztof Karas printf("\t %-20s: %10.2f %10.2f %10.2f", 330045c781dSRichael Zhuang job->name, (float)time_in_usec / SPDK_SEC_TO_USEC, io_per_second, mb_per_second); 331723dd06eSRichael Zhuang printf(" %10.2f %8.2f", 33229784f35SKrzysztof Karas failed_per_second, timeout_per_second); 333723dd06eSRichael Zhuang printf(" %10.2f %10.2f %10.2f\n", 334723dd06eSRichael Zhuang average_latency, min_latency, max_latency); 33529784f35SKrzysztof Karas 33629784f35SKrzysztof Karas stats->total_io_per_second += io_per_second; 33729784f35SKrzysztof Karas stats->total_mb_per_second += mb_per_second; 33829784f35SKrzysztof Karas stats->total_failed_per_second += failed_per_second; 33929784f35SKrzysztof Karas stats->total_timeout_per_second += timeout_per_second; 340723dd06eSRichael Zhuang stats->total_io_completed += job->io_completed + job->io_failed; 341723dd06eSRichael Zhuang stats->total_tsc += latency_info.total; 342723dd06eSRichael Zhuang if (min_latency < stats->min_latency) { 343723dd06eSRichael Zhuang stats->min_latency = min_latency; 344723dd06eSRichael Zhuang } 345723dd06eSRichael Zhuang if (max_latency > stats->max_latency) { 346723dd06eSRichael Zhuang stats->max_latency = max_latency; 347723dd06eSRichael Zhuang } 34829784f35SKrzysztof Karas } 34929784f35SKrzysztof Karas 35029784f35SKrzysztof Karas static void 35129784f35SKrzysztof Karas generate_data(void *buf, int buf_len, int block_size, void *md_buf, int md_size, 35229784f35SKrzysztof Karas int num_blocks) 35329784f35SKrzysztof Karas { 35429784f35SKrzysztof Karas int offset_blocks = 0, md_offset, data_block_size, inner_offset; 35529784f35SKrzysztof Karas 35629784f35SKrzysztof Karas if (buf_len < num_blocks * block_size) { 35729784f35SKrzysztof Karas return; 35829784f35SKrzysztof Karas } 35929784f35SKrzysztof Karas 36029784f35SKrzysztof Karas if (md_buf == NULL) { 36129784f35SKrzysztof Karas data_block_size = block_size - md_size; 36229784f35SKrzysztof Karas md_buf = (char *)buf + data_block_size; 36329784f35SKrzysztof Karas md_offset = block_size; 36429784f35SKrzysztof Karas } else { 36529784f35SKrzysztof Karas data_block_size = block_size; 36629784f35SKrzysztof Karas md_offset = md_size; 36729784f35SKrzysztof Karas } 36829784f35SKrzysztof Karas 36929784f35SKrzysztof Karas while (offset_blocks < num_blocks) { 37029784f35SKrzysztof Karas inner_offset = 0; 37129784f35SKrzysztof Karas while (inner_offset < data_block_size) { 37229784f35SKrzysztof Karas *(uint32_t *)buf = offset_blocks + inner_offset; 37329784f35SKrzysztof Karas inner_offset += sizeof(uint32_t); 37429784f35SKrzysztof Karas buf += sizeof(uint32_t); 37529784f35SKrzysztof Karas } 37629784f35SKrzysztof Karas memset(md_buf, offset_blocks, md_size); 37729784f35SKrzysztof Karas md_buf += md_offset; 37829784f35SKrzysztof Karas offset_blocks++; 37929784f35SKrzysztof Karas } 38029784f35SKrzysztof Karas } 38129784f35SKrzysztof Karas 38229784f35SKrzysztof Karas static bool 38329784f35SKrzysztof Karas copy_data(void *wr_buf, int wr_buf_len, void *rd_buf, int rd_buf_len, int block_size, 38429784f35SKrzysztof Karas void *wr_md_buf, void *rd_md_buf, int md_size, int num_blocks) 38529784f35SKrzysztof Karas { 38629784f35SKrzysztof Karas if (wr_buf_len < num_blocks * block_size || rd_buf_len < num_blocks * block_size) { 38729784f35SKrzysztof Karas return false; 38829784f35SKrzysztof Karas } 38929784f35SKrzysztof Karas 39029784f35SKrzysztof Karas assert((wr_md_buf != NULL) == (rd_md_buf != NULL)); 39129784f35SKrzysztof Karas 39229784f35SKrzysztof Karas memcpy(wr_buf, rd_buf, block_size * num_blocks); 39329784f35SKrzysztof Karas 39429784f35SKrzysztof Karas if (wr_md_buf != NULL) { 39529784f35SKrzysztof Karas memcpy(wr_md_buf, rd_md_buf, md_size * num_blocks); 39629784f35SKrzysztof Karas } 39729784f35SKrzysztof Karas 39829784f35SKrzysztof Karas return true; 39929784f35SKrzysztof Karas } 40029784f35SKrzysztof Karas 40129784f35SKrzysztof Karas static bool 40229784f35SKrzysztof Karas verify_data(void *wr_buf, int wr_buf_len, void *rd_buf, int rd_buf_len, int block_size, 40329784f35SKrzysztof Karas void *wr_md_buf, void *rd_md_buf, int md_size, int num_blocks, bool md_check) 40429784f35SKrzysztof Karas { 40529784f35SKrzysztof Karas int offset_blocks = 0, md_offset, data_block_size; 40629784f35SKrzysztof Karas 40729784f35SKrzysztof Karas if (wr_buf_len < num_blocks * block_size || rd_buf_len < num_blocks * block_size) { 40829784f35SKrzysztof Karas return false; 40929784f35SKrzysztof Karas } 41029784f35SKrzysztof Karas 41129784f35SKrzysztof Karas assert((wr_md_buf != NULL) == (rd_md_buf != NULL)); 41229784f35SKrzysztof Karas 41329784f35SKrzysztof Karas if (wr_md_buf == NULL) { 41429784f35SKrzysztof Karas data_block_size = block_size - md_size; 41529784f35SKrzysztof Karas wr_md_buf = (char *)wr_buf + data_block_size; 41629784f35SKrzysztof Karas rd_md_buf = (char *)rd_buf + data_block_size; 41729784f35SKrzysztof Karas md_offset = block_size; 41829784f35SKrzysztof Karas } else { 41929784f35SKrzysztof Karas data_block_size = block_size; 42029784f35SKrzysztof Karas md_offset = md_size; 42129784f35SKrzysztof Karas } 42229784f35SKrzysztof Karas 42329784f35SKrzysztof Karas while (offset_blocks < num_blocks) { 42429784f35SKrzysztof Karas if (memcmp(wr_buf, rd_buf, data_block_size) != 0) { 42529784f35SKrzysztof Karas return false; 42629784f35SKrzysztof Karas } 42729784f35SKrzysztof Karas 42829784f35SKrzysztof Karas wr_buf += block_size; 42929784f35SKrzysztof Karas rd_buf += block_size; 43029784f35SKrzysztof Karas 43129784f35SKrzysztof Karas if (md_check) { 43229784f35SKrzysztof Karas if (memcmp(wr_md_buf, rd_md_buf, md_size) != 0) { 43329784f35SKrzysztof Karas return false; 43429784f35SKrzysztof Karas } 43529784f35SKrzysztof Karas 43629784f35SKrzysztof Karas wr_md_buf += md_offset; 43729784f35SKrzysztof Karas rd_md_buf += md_offset; 43829784f35SKrzysztof Karas } 43929784f35SKrzysztof Karas 44029784f35SKrzysztof Karas offset_blocks++; 44129784f35SKrzysztof Karas } 44229784f35SKrzysztof Karas 44329784f35SKrzysztof Karas return true; 44429784f35SKrzysztof Karas } 44529784f35SKrzysztof Karas 44629784f35SKrzysztof Karas static void 44729784f35SKrzysztof Karas free_job_config(void) 44829784f35SKrzysztof Karas { 44929784f35SKrzysztof Karas struct job_config *config, *tmp; 45029784f35SKrzysztof Karas 45129784f35SKrzysztof Karas spdk_conf_free(g_bdevperf_conf); 45229784f35SKrzysztof Karas g_bdevperf_conf = NULL; 45329784f35SKrzysztof Karas 45429784f35SKrzysztof Karas TAILQ_FOREACH_SAFE(config, &job_config_list, link, tmp) { 45529784f35SKrzysztof Karas TAILQ_REMOVE(&job_config_list, config, link); 45629784f35SKrzysztof Karas free(config); 45729784f35SKrzysztof Karas } 45829784f35SKrzysztof Karas } 45929784f35SKrzysztof Karas 46029784f35SKrzysztof Karas static void 461dcb296a3SJim Harris bdevperf_job_free(struct bdevperf_job *job) 462dcb296a3SJim Harris { 463723dd06eSRichael Zhuang spdk_histogram_data_free(job->histogram); 464dcb296a3SJim Harris spdk_bit_array_free(&job->outstanding); 465d254a3b9SSlawomir Ptak spdk_bit_array_free(&job->random_map); 466dcb296a3SJim Harris spdk_zipf_free(&job->zipf); 467dcb296a3SJim Harris free(job->name); 468dcb296a3SJim Harris free(job); 469dcb296a3SJim Harris } 470dcb296a3SJim Harris 471dcb296a3SJim Harris static void 472081b190bSJim Harris job_thread_exit(void *ctx) 473081b190bSJim Harris { 474081b190bSJim Harris spdk_thread_exit(spdk_get_thread()); 475081b190bSJim Harris } 476081b190bSJim Harris 477081b190bSJim Harris static void 478723dd06eSRichael Zhuang check_cutoff(void *ctx, uint64_t start, uint64_t end, uint64_t count, 479723dd06eSRichael Zhuang uint64_t total, uint64_t so_far) 480723dd06eSRichael Zhuang { 481723dd06eSRichael Zhuang double so_far_pct; 482723dd06eSRichael Zhuang double **cutoff = ctx; 483723dd06eSRichael Zhuang uint64_t tsc_rate; 484723dd06eSRichael Zhuang 485723dd06eSRichael Zhuang if (count == 0) { 486723dd06eSRichael Zhuang return; 487723dd06eSRichael Zhuang } 488723dd06eSRichael Zhuang 489723dd06eSRichael Zhuang tsc_rate = spdk_get_ticks_hz(); 490723dd06eSRichael Zhuang so_far_pct = (double)so_far / total; 491723dd06eSRichael Zhuang while (so_far_pct >= **cutoff && **cutoff > 0) { 492045c781dSRichael Zhuang printf("%9.5f%% : %9.3fus\n", **cutoff * 100, (double)end * SPDK_SEC_TO_USEC / tsc_rate); 493723dd06eSRichael Zhuang (*cutoff)++; 494723dd06eSRichael Zhuang } 495723dd06eSRichael Zhuang } 496723dd06eSRichael Zhuang 497723dd06eSRichael Zhuang static void 498723dd06eSRichael Zhuang print_bucket(void *ctx, uint64_t start, uint64_t end, uint64_t count, 499723dd06eSRichael Zhuang uint64_t total, uint64_t so_far) 500723dd06eSRichael Zhuang { 501723dd06eSRichael Zhuang double so_far_pct; 502723dd06eSRichael Zhuang uint64_t tsc_rate; 503723dd06eSRichael Zhuang 504723dd06eSRichael Zhuang if (count == 0) { 505723dd06eSRichael Zhuang return; 506723dd06eSRichael Zhuang } 507723dd06eSRichael Zhuang 508723dd06eSRichael Zhuang tsc_rate = spdk_get_ticks_hz(); 509723dd06eSRichael Zhuang so_far_pct = (double)so_far * 100 / total; 510723dd06eSRichael Zhuang printf("%9.3f - %9.3f: %9.4f%% (%9ju)\n", 511045c781dSRichael Zhuang (double)start * SPDK_SEC_TO_USEC / tsc_rate, 512045c781dSRichael Zhuang (double)end * SPDK_SEC_TO_USEC / tsc_rate, 513723dd06eSRichael Zhuang so_far_pct, count); 514723dd06eSRichael Zhuang } 515723dd06eSRichael Zhuang 516723dd06eSRichael Zhuang static void 51729784f35SKrzysztof Karas bdevperf_test_done(void *ctx) 51829784f35SKrzysztof Karas { 51929784f35SKrzysztof Karas struct bdevperf_job *job, *jtmp; 52029784f35SKrzysztof Karas struct bdevperf_task *task, *ttmp; 5218e373044SShuhei Matsumoto struct lcore_thread *lthread, *lttmp; 522723dd06eSRichael Zhuang double average_latency = 0.0; 52329784f35SKrzysztof Karas uint64_t time_in_usec; 524723dd06eSRichael Zhuang int rc; 52529784f35SKrzysztof Karas 52629784f35SKrzysztof Karas if (g_time_in_usec) { 52729784f35SKrzysztof Karas g_stats.io_time_in_usec = g_time_in_usec; 52829784f35SKrzysztof Karas 52929784f35SKrzysztof Karas if (!g_run_rc && g_performance_dump_active) { 53029784f35SKrzysztof Karas spdk_thread_send_msg(spdk_get_thread(), bdevperf_test_done, NULL); 53129784f35SKrzysztof Karas return; 53229784f35SKrzysztof Karas } 53329784f35SKrzysztof Karas } 53429784f35SKrzysztof Karas 53529784f35SKrzysztof Karas if (g_show_performance_real_time) { 53629784f35SKrzysztof Karas spdk_poller_unregister(&g_perf_timer); 53729784f35SKrzysztof Karas } 53829784f35SKrzysztof Karas 53929784f35SKrzysztof Karas if (g_shutdown) { 54029784f35SKrzysztof Karas g_shutdown_tsc = spdk_get_ticks() - g_start_tsc; 541045c781dSRichael Zhuang time_in_usec = g_shutdown_tsc * SPDK_SEC_TO_USEC / spdk_get_ticks_hz(); 54229784f35SKrzysztof Karas g_time_in_usec = (g_time_in_usec > time_in_usec) ? time_in_usec : g_time_in_usec; 54329784f35SKrzysztof Karas printf("Received shutdown signal, test time was about %.6f seconds\n", 544045c781dSRichael Zhuang (double)g_time_in_usec / SPDK_SEC_TO_USEC); 54529784f35SKrzysztof Karas } 54629784f35SKrzysztof Karas 547723dd06eSRichael Zhuang printf("\n%*s\n", 107, "Latency(us)"); 548723dd06eSRichael Zhuang printf("\r %-*s: %10s %10s %10s %10s %8s %10s %10s %10s\n", 549723dd06eSRichael Zhuang 28, "Device Information", "runtime(s)", "IOPS", "MiB/s", "Fail/s", "TO/s", "Average", "min", "max"); 55029784f35SKrzysztof Karas 55129784f35SKrzysztof Karas TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) { 55229784f35SKrzysztof Karas performance_dump_job(&g_stats, job); 553723dd06eSRichael Zhuang } 554723dd06eSRichael Zhuang 555723dd06eSRichael Zhuang printf("\r ==================================================================================" 556723dd06eSRichael Zhuang "=================================\n"); 557723dd06eSRichael Zhuang printf("\r %-28s: %10s %10.2f %10.2f", 558723dd06eSRichael Zhuang "Total", "", g_stats.total_io_per_second, g_stats.total_mb_per_second); 559723dd06eSRichael Zhuang printf(" %10.2f %8.2f", 560723dd06eSRichael Zhuang g_stats.total_failed_per_second, g_stats.total_timeout_per_second); 561723dd06eSRichael Zhuang 562723dd06eSRichael Zhuang if (g_stats.total_io_completed != 0) { 563045c781dSRichael Zhuang average_latency = ((double)g_stats.total_tsc / g_stats.total_io_completed) * SPDK_SEC_TO_USEC / 564723dd06eSRichael Zhuang spdk_get_ticks_hz(); 565723dd06eSRichael Zhuang } 566723dd06eSRichael Zhuang printf(" %10.2f %10.2f %10.2f\n", average_latency, g_stats.min_latency, g_stats.max_latency); 567723dd06eSRichael Zhuang 568723dd06eSRichael Zhuang fflush(stdout); 569723dd06eSRichael Zhuang 570723dd06eSRichael Zhuang if (g_latency_display_level == 0 || g_stats.total_io_completed == 0) { 571723dd06eSRichael Zhuang goto clean; 572723dd06eSRichael Zhuang } 573723dd06eSRichael Zhuang 574723dd06eSRichael Zhuang printf("\n Latency summary\n"); 575723dd06eSRichael Zhuang TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) { 576723dd06eSRichael Zhuang printf("\r =============================================\n"); 57772da547aSShuhei Matsumoto printf("\r Job: %s (Core Mask 0x%s)\n", job->name, 578723dd06eSRichael Zhuang spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread))); 579723dd06eSRichael Zhuang 580723dd06eSRichael Zhuang const double *cutoff = g_latency_cutoffs; 581723dd06eSRichael Zhuang 582723dd06eSRichael Zhuang spdk_histogram_data_iterate(job->histogram, check_cutoff, &cutoff); 583723dd06eSRichael Zhuang 584723dd06eSRichael Zhuang printf("\n"); 585723dd06eSRichael Zhuang } 586723dd06eSRichael Zhuang 587723dd06eSRichael Zhuang if (g_latency_display_level == 1) { 588723dd06eSRichael Zhuang goto clean; 589723dd06eSRichael Zhuang } 590723dd06eSRichael Zhuang 591723dd06eSRichael Zhuang printf("\r Latency histogram\n"); 592723dd06eSRichael Zhuang TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) { 593723dd06eSRichael Zhuang printf("\r =============================================\n"); 59472da547aSShuhei Matsumoto printf("\r Job: %s (Core Mask 0x%s)\n", job->name, 595723dd06eSRichael Zhuang spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread))); 596723dd06eSRichael Zhuang 597723dd06eSRichael Zhuang spdk_histogram_data_iterate(job->histogram, print_bucket, NULL); 598723dd06eSRichael Zhuang printf("\n"); 599723dd06eSRichael Zhuang } 600723dd06eSRichael Zhuang 601723dd06eSRichael Zhuang clean: 602723dd06eSRichael Zhuang TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) { 603723dd06eSRichael Zhuang TAILQ_REMOVE(&g_bdevperf.jobs, job, link); 60429784f35SKrzysztof Karas 6058e373044SShuhei Matsumoto if (!g_one_thread_per_lcore) { 606081b190bSJim Harris spdk_thread_send_msg(job->thread, job_thread_exit, NULL); 6078e373044SShuhei Matsumoto } 608081b190bSJim Harris 60929784f35SKrzysztof Karas TAILQ_FOREACH_SAFE(task, &job->task_list, link, ttmp) { 61029784f35SKrzysztof Karas TAILQ_REMOVE(&job->task_list, task, link); 61129784f35SKrzysztof Karas spdk_free(task->buf); 61229784f35SKrzysztof Karas spdk_free(task->md_buf); 61329784f35SKrzysztof Karas free(task); 61429784f35SKrzysztof Karas } 61529784f35SKrzysztof Karas 616dcb296a3SJim Harris bdevperf_job_free(job); 61729784f35SKrzysztof Karas } 61829784f35SKrzysztof Karas 6198e373044SShuhei Matsumoto if (g_one_thread_per_lcore) { 6208e373044SShuhei Matsumoto TAILQ_FOREACH_SAFE(lthread, &g_lcore_thread_list, link, lttmp) { 6218e373044SShuhei Matsumoto TAILQ_REMOVE(&g_lcore_thread_list, lthread, link); 6228e373044SShuhei Matsumoto spdk_thread_send_msg(lthread->thread, job_thread_exit, NULL); 6238e373044SShuhei Matsumoto free(lthread); 6248e373044SShuhei Matsumoto } 6258e373044SShuhei Matsumoto } 6268e373044SShuhei Matsumoto 62729784f35SKrzysztof Karas rc = g_run_rc; 62829784f35SKrzysztof Karas if (g_request && !g_shutdown) { 62929784f35SKrzysztof Karas rpc_perform_tests_cb(); 63029784f35SKrzysztof Karas if (rc != 0) { 63129784f35SKrzysztof Karas spdk_app_stop(rc); 63229784f35SKrzysztof Karas } 63329784f35SKrzysztof Karas } else { 63429784f35SKrzysztof Karas spdk_app_stop(rc); 63529784f35SKrzysztof Karas } 63629784f35SKrzysztof Karas } 63729784f35SKrzysztof Karas 63829784f35SKrzysztof Karas static void 63929784f35SKrzysztof Karas bdevperf_job_end(void *ctx) 64029784f35SKrzysztof Karas { 64129784f35SKrzysztof Karas assert(g_main_thread == spdk_get_thread()); 64229784f35SKrzysztof Karas 64329784f35SKrzysztof Karas if (--g_bdevperf.running_jobs == 0) { 64429784f35SKrzysztof Karas bdevperf_test_done(NULL); 64529784f35SKrzysztof Karas } 64629784f35SKrzysztof Karas } 64729784f35SKrzysztof Karas 64829784f35SKrzysztof Karas static void 649723dd06eSRichael Zhuang bdevperf_channel_get_histogram_cb(void *cb_arg, int status, struct spdk_histogram_data *histogram) 650723dd06eSRichael Zhuang { 651723dd06eSRichael Zhuang struct spdk_histogram_data *job_hist = cb_arg; 65260fd5c55SGangCao 65360fd5c55SGangCao if (status == 0) { 654723dd06eSRichael Zhuang spdk_histogram_data_merge(job_hist, histogram); 655723dd06eSRichael Zhuang } 65660fd5c55SGangCao } 657723dd06eSRichael Zhuang 658723dd06eSRichael Zhuang static void 659893aaaccSAlexey Marchuk bdevperf_job_empty(struct bdevperf_job *job) 660893aaaccSAlexey Marchuk { 661893aaaccSAlexey Marchuk uint64_t end_tsc = 0; 662893aaaccSAlexey Marchuk 663893aaaccSAlexey Marchuk end_tsc = spdk_get_ticks() - g_start_tsc; 664893aaaccSAlexey Marchuk job->run_time_in_usec = end_tsc * SPDK_SEC_TO_USEC / spdk_get_ticks_hz(); 665893aaaccSAlexey Marchuk /* keep histogram info before channel is destroyed */ 666893aaaccSAlexey Marchuk spdk_bdev_channel_get_histogram(job->ch, bdevperf_channel_get_histogram_cb, 667893aaaccSAlexey Marchuk job->histogram); 668893aaaccSAlexey Marchuk spdk_put_io_channel(job->ch); 669893aaaccSAlexey Marchuk spdk_bdev_close(job->bdev_desc); 670893aaaccSAlexey Marchuk spdk_thread_send_msg(g_main_thread, bdevperf_job_end, NULL); 671893aaaccSAlexey Marchuk } 672893aaaccSAlexey Marchuk 673893aaaccSAlexey Marchuk static void 67429784f35SKrzysztof Karas bdevperf_end_task(struct bdevperf_task *task) 67529784f35SKrzysztof Karas { 67629784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 67729784f35SKrzysztof Karas 67829784f35SKrzysztof Karas TAILQ_INSERT_TAIL(&job->task_list, task, link); 67929784f35SKrzysztof Karas if (job->is_draining) { 68029784f35SKrzysztof Karas if (job->current_queue_depth == 0) { 681893aaaccSAlexey Marchuk bdevperf_job_empty(job); 68229784f35SKrzysztof Karas } 68329784f35SKrzysztof Karas } 68429784f35SKrzysztof Karas } 68529784f35SKrzysztof Karas 68629784f35SKrzysztof Karas static void 68729784f35SKrzysztof Karas bdevperf_queue_io_wait_with_cb(struct bdevperf_task *task, spdk_bdev_io_wait_cb cb_fn) 68829784f35SKrzysztof Karas { 68929784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 69029784f35SKrzysztof Karas 69129784f35SKrzysztof Karas task->bdev_io_wait.bdev = job->bdev; 69229784f35SKrzysztof Karas task->bdev_io_wait.cb_fn = cb_fn; 69329784f35SKrzysztof Karas task->bdev_io_wait.cb_arg = task; 69429784f35SKrzysztof Karas spdk_bdev_queue_io_wait(job->bdev, job->ch, &task->bdev_io_wait); 69529784f35SKrzysztof Karas } 69629784f35SKrzysztof Karas 69729784f35SKrzysztof Karas static int 69829784f35SKrzysztof Karas bdevperf_job_drain(void *ctx) 69929784f35SKrzysztof Karas { 70029784f35SKrzysztof Karas struct bdevperf_job *job = ctx; 70129784f35SKrzysztof Karas 70229784f35SKrzysztof Karas spdk_poller_unregister(&job->run_timer); 70329784f35SKrzysztof Karas if (job->reset) { 70429784f35SKrzysztof Karas spdk_poller_unregister(&job->reset_timer); 70529784f35SKrzysztof Karas } 70629784f35SKrzysztof Karas 70729784f35SKrzysztof Karas job->is_draining = true; 70829784f35SKrzysztof Karas 70929784f35SKrzysztof Karas return -1; 71029784f35SKrzysztof Karas } 71129784f35SKrzysztof Karas 712893aaaccSAlexey Marchuk static int 713893aaaccSAlexey Marchuk bdevperf_job_drain_timer(void *ctx) 714893aaaccSAlexey Marchuk { 715893aaaccSAlexey Marchuk struct bdevperf_job *job = ctx; 716893aaaccSAlexey Marchuk 717893aaaccSAlexey Marchuk bdevperf_job_drain(ctx); 718893aaaccSAlexey Marchuk if (job->current_queue_depth == 0) { 719893aaaccSAlexey Marchuk bdevperf_job_empty(job); 720893aaaccSAlexey Marchuk } 721893aaaccSAlexey Marchuk 722893aaaccSAlexey Marchuk return SPDK_POLLER_BUSY; 723893aaaccSAlexey Marchuk } 724893aaaccSAlexey Marchuk 72529784f35SKrzysztof Karas static void 72629784f35SKrzysztof Karas bdevperf_abort_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 72729784f35SKrzysztof Karas { 72829784f35SKrzysztof Karas struct bdevperf_task *task = cb_arg; 72929784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 73029784f35SKrzysztof Karas 73129784f35SKrzysztof Karas job->current_queue_depth--; 73229784f35SKrzysztof Karas 73329784f35SKrzysztof Karas if (success) { 73429784f35SKrzysztof Karas job->io_completed++; 73529784f35SKrzysztof Karas } else { 73629784f35SKrzysztof Karas job->io_failed++; 73729784f35SKrzysztof Karas if (!job->continue_on_failure) { 73829784f35SKrzysztof Karas bdevperf_job_drain(job); 73929784f35SKrzysztof Karas g_run_rc = -1; 74029784f35SKrzysztof Karas } 74129784f35SKrzysztof Karas } 74229784f35SKrzysztof Karas 74329784f35SKrzysztof Karas spdk_bdev_free_io(bdev_io); 74429784f35SKrzysztof Karas bdevperf_end_task(task); 74529784f35SKrzysztof Karas } 74629784f35SKrzysztof Karas 74729784f35SKrzysztof Karas static int 74829784f35SKrzysztof Karas bdevperf_verify_dif(struct bdevperf_task *task, struct iovec *iovs, int iovcnt) 74929784f35SKrzysztof Karas { 75029784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 75129784f35SKrzysztof Karas struct spdk_bdev *bdev = job->bdev; 75229784f35SKrzysztof Karas struct spdk_dif_ctx dif_ctx; 75329784f35SKrzysztof Karas struct spdk_dif_error err_blk = {}; 75429784f35SKrzysztof Karas int rc; 755a711d629SSlawomir Ptak struct spdk_dif_ctx_init_ext_opts dif_opts; 75629784f35SKrzysztof Karas 7575681a8a6SKonrad Sztyber dif_opts.size = SPDK_SIZEOF(&dif_opts, dif_pi_format); 758a711d629SSlawomir Ptak dif_opts.dif_pi_format = SPDK_DIF_PI_FORMAT_16; 75929784f35SKrzysztof Karas rc = spdk_dif_ctx_init(&dif_ctx, 76029784f35SKrzysztof Karas spdk_bdev_get_block_size(bdev), 76129784f35SKrzysztof Karas spdk_bdev_get_md_size(bdev), 76229784f35SKrzysztof Karas spdk_bdev_is_md_interleaved(bdev), 76329784f35SKrzysztof Karas spdk_bdev_is_dif_head_of_md(bdev), 76429784f35SKrzysztof Karas spdk_bdev_get_dif_type(bdev), 76529784f35SKrzysztof Karas job->dif_check_flags, 766a711d629SSlawomir Ptak task->offset_blocks, 0, 0, 0, 0, &dif_opts); 76729784f35SKrzysztof Karas if (rc != 0) { 76829784f35SKrzysztof Karas fprintf(stderr, "Initialization of DIF context failed\n"); 76929784f35SKrzysztof Karas return rc; 77029784f35SKrzysztof Karas } 77129784f35SKrzysztof Karas 77229784f35SKrzysztof Karas if (spdk_bdev_is_md_interleaved(bdev)) { 77329784f35SKrzysztof Karas rc = spdk_dif_verify(iovs, iovcnt, job->io_size_blocks, &dif_ctx, &err_blk); 77429784f35SKrzysztof Karas } else { 77529784f35SKrzysztof Karas struct iovec md_iov = { 77629784f35SKrzysztof Karas .iov_base = task->md_buf, 77729784f35SKrzysztof Karas .iov_len = spdk_bdev_get_md_size(bdev) * job->io_size_blocks, 77829784f35SKrzysztof Karas }; 77929784f35SKrzysztof Karas 78029784f35SKrzysztof Karas rc = spdk_dix_verify(iovs, iovcnt, &md_iov, job->io_size_blocks, &dif_ctx, &err_blk); 78129784f35SKrzysztof Karas } 78229784f35SKrzysztof Karas 78329784f35SKrzysztof Karas if (rc != 0) { 78429784f35SKrzysztof Karas fprintf(stderr, "DIF/DIX error detected. type=%d, offset=%" PRIu32 "\n", 78529784f35SKrzysztof Karas err_blk.err_type, err_blk.err_offset); 78629784f35SKrzysztof Karas } 78729784f35SKrzysztof Karas 78829784f35SKrzysztof Karas return rc; 78929784f35SKrzysztof Karas } 79029784f35SKrzysztof Karas 79129784f35SKrzysztof Karas static void 79229784f35SKrzysztof Karas bdevperf_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 79329784f35SKrzysztof Karas { 79429784f35SKrzysztof Karas struct bdevperf_job *job; 79529784f35SKrzysztof Karas struct bdevperf_task *task = cb_arg; 79629784f35SKrzysztof Karas struct iovec *iovs; 79729784f35SKrzysztof Karas int iovcnt; 79829784f35SKrzysztof Karas bool md_check; 79929784f35SKrzysztof Karas uint64_t offset_in_ios; 80029784f35SKrzysztof Karas int rc; 80129784f35SKrzysztof Karas 80229784f35SKrzysztof Karas job = task->job; 80329784f35SKrzysztof Karas md_check = spdk_bdev_get_dif_type(job->bdev) == SPDK_DIF_DISABLE; 80429784f35SKrzysztof Karas 80529784f35SKrzysztof Karas if (g_error_to_exit == true) { 80629784f35SKrzysztof Karas bdevperf_job_drain(job); 80729784f35SKrzysztof Karas } else if (!success) { 80829784f35SKrzysztof Karas if (!job->reset && !job->continue_on_failure) { 80929784f35SKrzysztof Karas bdevperf_job_drain(job); 81029784f35SKrzysztof Karas g_run_rc = -1; 81129784f35SKrzysztof Karas g_error_to_exit = true; 81229784f35SKrzysztof Karas printf("task offset: %" PRIu64 " on job bdev=%s fails\n", 81329784f35SKrzysztof Karas task->offset_blocks, job->name); 81429784f35SKrzysztof Karas } 81529784f35SKrzysztof Karas } else if (job->verify || job->reset) { 81629784f35SKrzysztof Karas spdk_bdev_io_get_iovec(bdev_io, &iovs, &iovcnt); 81729784f35SKrzysztof Karas assert(iovcnt == 1); 81829784f35SKrzysztof Karas assert(iovs != NULL); 81929784f35SKrzysztof Karas if (!verify_data(task->buf, job->buf_size, iovs[0].iov_base, iovs[0].iov_len, 82029784f35SKrzysztof Karas spdk_bdev_get_block_size(job->bdev), 82129784f35SKrzysztof Karas task->md_buf, spdk_bdev_io_get_md_buf(bdev_io), 82229784f35SKrzysztof Karas spdk_bdev_get_md_size(job->bdev), 82329784f35SKrzysztof Karas job->io_size_blocks, md_check)) { 82429784f35SKrzysztof Karas printf("Buffer mismatch! Target: %s Disk Offset: %" PRIu64 "\n", job->name, task->offset_blocks); 82529784f35SKrzysztof Karas printf(" First dword expected 0x%x got 0x%x\n", *(int *)task->buf, *(int *)iovs[0].iov_base); 82629784f35SKrzysztof Karas bdevperf_job_drain(job); 82729784f35SKrzysztof Karas g_run_rc = -1; 82829784f35SKrzysztof Karas } 82929784f35SKrzysztof Karas } else if (job->dif_check_flags != 0) { 83029784f35SKrzysztof Karas if (task->io_type == SPDK_BDEV_IO_TYPE_READ && spdk_bdev_get_md_size(job->bdev) != 0) { 83129784f35SKrzysztof Karas spdk_bdev_io_get_iovec(bdev_io, &iovs, &iovcnt); 83229784f35SKrzysztof Karas assert(iovcnt == 1); 83329784f35SKrzysztof Karas assert(iovs != NULL); 83429784f35SKrzysztof Karas rc = bdevperf_verify_dif(task, iovs, iovcnt); 83529784f35SKrzysztof Karas if (rc != 0) { 83629784f35SKrzysztof Karas printf("DIF error detected. task offset: %" PRIu64 " on job bdev=%s\n", 83729784f35SKrzysztof Karas task->offset_blocks, job->name); 83829784f35SKrzysztof Karas 83929784f35SKrzysztof Karas success = false; 84029784f35SKrzysztof Karas if (!job->reset && !job->continue_on_failure) { 84129784f35SKrzysztof Karas bdevperf_job_drain(job); 84229784f35SKrzysztof Karas g_run_rc = -1; 84329784f35SKrzysztof Karas g_error_to_exit = true; 84429784f35SKrzysztof Karas } 84529784f35SKrzysztof Karas } 84629784f35SKrzysztof Karas } 84729784f35SKrzysztof Karas } 84829784f35SKrzysztof Karas 84929784f35SKrzysztof Karas job->current_queue_depth--; 85029784f35SKrzysztof Karas 85129784f35SKrzysztof Karas if (success) { 85229784f35SKrzysztof Karas job->io_completed++; 85329784f35SKrzysztof Karas } else { 85429784f35SKrzysztof Karas job->io_failed++; 85529784f35SKrzysztof Karas } 85629784f35SKrzysztof Karas 85729784f35SKrzysztof Karas if (job->verify) { 85829784f35SKrzysztof Karas assert(task->offset_blocks / job->io_size_blocks >= job->ios_base); 85929784f35SKrzysztof Karas offset_in_ios = task->offset_blocks / job->io_size_blocks - job->ios_base; 86029784f35SKrzysztof Karas 86129784f35SKrzysztof Karas assert(spdk_bit_array_get(job->outstanding, offset_in_ios) == true); 86229784f35SKrzysztof Karas spdk_bit_array_clear(job->outstanding, offset_in_ios); 86329784f35SKrzysztof Karas } 86429784f35SKrzysztof Karas 86529784f35SKrzysztof Karas spdk_bdev_free_io(bdev_io); 86629784f35SKrzysztof Karas 86729784f35SKrzysztof Karas /* 86829784f35SKrzysztof Karas * is_draining indicates when time has expired for the test run 86929784f35SKrzysztof Karas * and we are just waiting for the previously submitted I/O 87029784f35SKrzysztof Karas * to complete. In this case, do not submit a new I/O to replace 87129784f35SKrzysztof Karas * the one just completed. 87229784f35SKrzysztof Karas */ 87329784f35SKrzysztof Karas if (!job->is_draining) { 87429784f35SKrzysztof Karas bdevperf_submit_single(job, task); 87529784f35SKrzysztof Karas } else { 87629784f35SKrzysztof Karas bdevperf_end_task(task); 87729784f35SKrzysztof Karas } 87829784f35SKrzysztof Karas } 87929784f35SKrzysztof Karas 88029784f35SKrzysztof Karas static void 88129784f35SKrzysztof Karas bdevperf_verify_submit_read(void *cb_arg) 88229784f35SKrzysztof Karas { 88329784f35SKrzysztof Karas struct bdevperf_job *job; 88429784f35SKrzysztof Karas struct bdevperf_task *task = cb_arg; 88529784f35SKrzysztof Karas int rc; 88629784f35SKrzysztof Karas 88729784f35SKrzysztof Karas job = task->job; 88829784f35SKrzysztof Karas 88929784f35SKrzysztof Karas /* Read the data back in */ 89029784f35SKrzysztof Karas rc = spdk_bdev_read_blocks_with_md(job->bdev_desc, job->ch, NULL, NULL, 89129784f35SKrzysztof Karas task->offset_blocks, job->io_size_blocks, 89229784f35SKrzysztof Karas bdevperf_complete, task); 89329784f35SKrzysztof Karas 89429784f35SKrzysztof Karas if (rc == -ENOMEM) { 89529784f35SKrzysztof Karas bdevperf_queue_io_wait_with_cb(task, bdevperf_verify_submit_read); 89629784f35SKrzysztof Karas } else if (rc != 0) { 89729784f35SKrzysztof Karas printf("Failed to submit read: %d\n", rc); 89829784f35SKrzysztof Karas bdevperf_job_drain(job); 89929784f35SKrzysztof Karas g_run_rc = rc; 90029784f35SKrzysztof Karas } 90129784f35SKrzysztof Karas } 90229784f35SKrzysztof Karas 90329784f35SKrzysztof Karas static void 90429784f35SKrzysztof Karas bdevperf_verify_write_complete(struct spdk_bdev_io *bdev_io, bool success, 90529784f35SKrzysztof Karas void *cb_arg) 90629784f35SKrzysztof Karas { 90729784f35SKrzysztof Karas if (success) { 90829784f35SKrzysztof Karas spdk_bdev_free_io(bdev_io); 90929784f35SKrzysztof Karas bdevperf_verify_submit_read(cb_arg); 91029784f35SKrzysztof Karas } else { 91129784f35SKrzysztof Karas bdevperf_complete(bdev_io, success, cb_arg); 91229784f35SKrzysztof Karas } 91329784f35SKrzysztof Karas } 91429784f35SKrzysztof Karas 91529784f35SKrzysztof Karas static void 91629784f35SKrzysztof Karas bdevperf_zcopy_populate_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 91729784f35SKrzysztof Karas { 91829784f35SKrzysztof Karas if (!success) { 91929784f35SKrzysztof Karas bdevperf_complete(bdev_io, success, cb_arg); 92029784f35SKrzysztof Karas return; 92129784f35SKrzysztof Karas } 92229784f35SKrzysztof Karas 92329784f35SKrzysztof Karas spdk_bdev_zcopy_end(bdev_io, false, bdevperf_complete, cb_arg); 92429784f35SKrzysztof Karas } 92529784f35SKrzysztof Karas 92629784f35SKrzysztof Karas static int 92729784f35SKrzysztof Karas bdevperf_generate_dif(struct bdevperf_task *task) 92829784f35SKrzysztof Karas { 92929784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 93029784f35SKrzysztof Karas struct spdk_bdev *bdev = job->bdev; 93129784f35SKrzysztof Karas struct spdk_dif_ctx dif_ctx; 93229784f35SKrzysztof Karas int rc; 933a711d629SSlawomir Ptak struct spdk_dif_ctx_init_ext_opts dif_opts; 93429784f35SKrzysztof Karas 9355681a8a6SKonrad Sztyber dif_opts.size = SPDK_SIZEOF(&dif_opts, dif_pi_format); 936a711d629SSlawomir Ptak dif_opts.dif_pi_format = SPDK_DIF_PI_FORMAT_16; 93729784f35SKrzysztof Karas rc = spdk_dif_ctx_init(&dif_ctx, 93829784f35SKrzysztof Karas spdk_bdev_get_block_size(bdev), 93929784f35SKrzysztof Karas spdk_bdev_get_md_size(bdev), 94029784f35SKrzysztof Karas spdk_bdev_is_md_interleaved(bdev), 94129784f35SKrzysztof Karas spdk_bdev_is_dif_head_of_md(bdev), 94229784f35SKrzysztof Karas spdk_bdev_get_dif_type(bdev), 94329784f35SKrzysztof Karas job->dif_check_flags, 944a711d629SSlawomir Ptak task->offset_blocks, 0, 0, 0, 0, &dif_opts); 94529784f35SKrzysztof Karas if (rc != 0) { 94629784f35SKrzysztof Karas fprintf(stderr, "Initialization of DIF context failed\n"); 94729784f35SKrzysztof Karas return rc; 94829784f35SKrzysztof Karas } 94929784f35SKrzysztof Karas 95029784f35SKrzysztof Karas if (spdk_bdev_is_md_interleaved(bdev)) { 95129784f35SKrzysztof Karas rc = spdk_dif_generate(&task->iov, 1, job->io_size_blocks, &dif_ctx); 95229784f35SKrzysztof Karas } else { 95329784f35SKrzysztof Karas struct iovec md_iov = { 95429784f35SKrzysztof Karas .iov_base = task->md_buf, 95529784f35SKrzysztof Karas .iov_len = spdk_bdev_get_md_size(bdev) * job->io_size_blocks, 95629784f35SKrzysztof Karas }; 95729784f35SKrzysztof Karas 95829784f35SKrzysztof Karas rc = spdk_dix_generate(&task->iov, 1, &md_iov, job->io_size_blocks, &dif_ctx); 95929784f35SKrzysztof Karas } 96029784f35SKrzysztof Karas 96129784f35SKrzysztof Karas if (rc != 0) { 96229784f35SKrzysztof Karas fprintf(stderr, "Generation of DIF/DIX failed\n"); 96329784f35SKrzysztof Karas } 96429784f35SKrzysztof Karas 96529784f35SKrzysztof Karas return rc; 96629784f35SKrzysztof Karas } 96729784f35SKrzysztof Karas 96829784f35SKrzysztof Karas static void 96929784f35SKrzysztof Karas bdevperf_submit_task(void *arg) 97029784f35SKrzysztof Karas { 97129784f35SKrzysztof Karas struct bdevperf_task *task = arg; 97229784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 97329784f35SKrzysztof Karas struct spdk_bdev_desc *desc; 97429784f35SKrzysztof Karas struct spdk_io_channel *ch; 97529784f35SKrzysztof Karas spdk_bdev_io_completion_cb cb_fn; 97629784f35SKrzysztof Karas uint64_t offset_in_ios; 97729784f35SKrzysztof Karas int rc = 0; 97829784f35SKrzysztof Karas 97929784f35SKrzysztof Karas desc = job->bdev_desc; 98029784f35SKrzysztof Karas ch = job->ch; 98129784f35SKrzysztof Karas 98229784f35SKrzysztof Karas switch (task->io_type) { 98329784f35SKrzysztof Karas case SPDK_BDEV_IO_TYPE_WRITE: 98429784f35SKrzysztof Karas if (spdk_bdev_get_md_size(job->bdev) != 0 && job->dif_check_flags != 0) { 98529784f35SKrzysztof Karas rc = bdevperf_generate_dif(task); 98629784f35SKrzysztof Karas } 98729784f35SKrzysztof Karas if (rc == 0) { 98829784f35SKrzysztof Karas cb_fn = (job->verify || job->reset) ? bdevperf_verify_write_complete : bdevperf_complete; 98929784f35SKrzysztof Karas 99029784f35SKrzysztof Karas if (g_zcopy) { 99129784f35SKrzysztof Karas spdk_bdev_zcopy_end(task->bdev_io, true, cb_fn, task); 99229784f35SKrzysztof Karas return; 99329784f35SKrzysztof Karas } else { 99429784f35SKrzysztof Karas rc = spdk_bdev_writev_blocks_with_md(desc, ch, &task->iov, 1, 99529784f35SKrzysztof Karas task->md_buf, 99629784f35SKrzysztof Karas task->offset_blocks, 99729784f35SKrzysztof Karas job->io_size_blocks, 99829784f35SKrzysztof Karas cb_fn, task); 99929784f35SKrzysztof Karas } 100029784f35SKrzysztof Karas } 100129784f35SKrzysztof Karas break; 100229784f35SKrzysztof Karas case SPDK_BDEV_IO_TYPE_FLUSH: 100329784f35SKrzysztof Karas rc = spdk_bdev_flush_blocks(desc, ch, task->offset_blocks, 100429784f35SKrzysztof Karas job->io_size_blocks, bdevperf_complete, task); 100529784f35SKrzysztof Karas break; 100629784f35SKrzysztof Karas case SPDK_BDEV_IO_TYPE_UNMAP: 100729784f35SKrzysztof Karas rc = spdk_bdev_unmap_blocks(desc, ch, task->offset_blocks, 100829784f35SKrzysztof Karas job->io_size_blocks, bdevperf_complete, task); 100929784f35SKrzysztof Karas break; 101029784f35SKrzysztof Karas case SPDK_BDEV_IO_TYPE_WRITE_ZEROES: 101129784f35SKrzysztof Karas rc = spdk_bdev_write_zeroes_blocks(desc, ch, task->offset_blocks, 101229784f35SKrzysztof Karas job->io_size_blocks, bdevperf_complete, task); 101329784f35SKrzysztof Karas break; 101429784f35SKrzysztof Karas case SPDK_BDEV_IO_TYPE_READ: 101529784f35SKrzysztof Karas if (g_zcopy) { 101629784f35SKrzysztof Karas rc = spdk_bdev_zcopy_start(desc, ch, NULL, 0, task->offset_blocks, job->io_size_blocks, 101729784f35SKrzysztof Karas true, bdevperf_zcopy_populate_complete, task); 101829784f35SKrzysztof Karas } else { 101929784f35SKrzysztof Karas rc = spdk_bdev_read_blocks_with_md(desc, ch, task->buf, task->md_buf, 102029784f35SKrzysztof Karas task->offset_blocks, 102129784f35SKrzysztof Karas job->io_size_blocks, 102229784f35SKrzysztof Karas bdevperf_complete, task); 102329784f35SKrzysztof Karas } 102429784f35SKrzysztof Karas break; 102529784f35SKrzysztof Karas case SPDK_BDEV_IO_TYPE_ABORT: 102629784f35SKrzysztof Karas rc = spdk_bdev_abort(desc, ch, task->task_to_abort, bdevperf_abort_complete, task); 102729784f35SKrzysztof Karas break; 102829784f35SKrzysztof Karas default: 102929784f35SKrzysztof Karas assert(false); 103029784f35SKrzysztof Karas rc = -EINVAL; 103129784f35SKrzysztof Karas break; 103229784f35SKrzysztof Karas } 103329784f35SKrzysztof Karas 103429784f35SKrzysztof Karas if (rc == -ENOMEM) { 103529784f35SKrzysztof Karas bdevperf_queue_io_wait_with_cb(task, bdevperf_submit_task); 103629784f35SKrzysztof Karas return; 103729784f35SKrzysztof Karas } else if (rc != 0) { 103829784f35SKrzysztof Karas printf("Failed to submit bdev_io: %d\n", rc); 103929784f35SKrzysztof Karas if (job->verify) { 104029784f35SKrzysztof Karas assert(task->offset_blocks / job->io_size_blocks >= job->ios_base); 104129784f35SKrzysztof Karas offset_in_ios = task->offset_blocks / job->io_size_blocks - job->ios_base; 104229784f35SKrzysztof Karas 104329784f35SKrzysztof Karas assert(spdk_bit_array_get(job->outstanding, offset_in_ios) == true); 104429784f35SKrzysztof Karas spdk_bit_array_clear(job->outstanding, offset_in_ios); 104529784f35SKrzysztof Karas } 104629784f35SKrzysztof Karas bdevperf_job_drain(job); 104729784f35SKrzysztof Karas g_run_rc = rc; 104829784f35SKrzysztof Karas return; 104929784f35SKrzysztof Karas } 105029784f35SKrzysztof Karas 105129784f35SKrzysztof Karas job->current_queue_depth++; 105229784f35SKrzysztof Karas } 105329784f35SKrzysztof Karas 105429784f35SKrzysztof Karas static void 105529784f35SKrzysztof Karas bdevperf_zcopy_get_buf_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 105629784f35SKrzysztof Karas { 105729784f35SKrzysztof Karas struct bdevperf_task *task = cb_arg; 105829784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 105929784f35SKrzysztof Karas struct iovec *iovs; 106029784f35SKrzysztof Karas int iovcnt; 106129784f35SKrzysztof Karas 106229784f35SKrzysztof Karas if (!success) { 106329784f35SKrzysztof Karas bdevperf_job_drain(job); 106429784f35SKrzysztof Karas g_run_rc = -1; 106529784f35SKrzysztof Karas return; 106629784f35SKrzysztof Karas } 106729784f35SKrzysztof Karas 106829784f35SKrzysztof Karas task->bdev_io = bdev_io; 106929784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_WRITE; 107029784f35SKrzysztof Karas 107129784f35SKrzysztof Karas if (job->verify || job->reset) { 107229784f35SKrzysztof Karas /* When job->verify or job->reset is enabled, task->buf is used for 107329784f35SKrzysztof Karas * verification of read after write. For write I/O, when zcopy APIs 107429784f35SKrzysztof Karas * are used, task->buf cannot be used, and data must be written to 107529784f35SKrzysztof Karas * the data buffer allocated underneath bdev layer instead. 107629784f35SKrzysztof Karas * Hence we copy task->buf to the allocated data buffer here. 107729784f35SKrzysztof Karas */ 107829784f35SKrzysztof Karas spdk_bdev_io_get_iovec(bdev_io, &iovs, &iovcnt); 107929784f35SKrzysztof Karas assert(iovcnt == 1); 108029784f35SKrzysztof Karas assert(iovs != NULL); 108129784f35SKrzysztof Karas 108229784f35SKrzysztof Karas copy_data(iovs[0].iov_base, iovs[0].iov_len, task->buf, job->buf_size, 108329784f35SKrzysztof Karas spdk_bdev_get_block_size(job->bdev), 108429784f35SKrzysztof Karas spdk_bdev_io_get_md_buf(bdev_io), task->md_buf, 108529784f35SKrzysztof Karas spdk_bdev_get_md_size(job->bdev), job->io_size_blocks); 108629784f35SKrzysztof Karas } 108729784f35SKrzysztof Karas 108829784f35SKrzysztof Karas bdevperf_submit_task(task); 108929784f35SKrzysztof Karas } 109029784f35SKrzysztof Karas 109129784f35SKrzysztof Karas static void 109229784f35SKrzysztof Karas bdevperf_prep_zcopy_write_task(void *arg) 109329784f35SKrzysztof Karas { 109429784f35SKrzysztof Karas struct bdevperf_task *task = arg; 109529784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 109629784f35SKrzysztof Karas int rc; 109729784f35SKrzysztof Karas 109829784f35SKrzysztof Karas rc = spdk_bdev_zcopy_start(job->bdev_desc, job->ch, NULL, 0, 109929784f35SKrzysztof Karas task->offset_blocks, job->io_size_blocks, 110029784f35SKrzysztof Karas false, bdevperf_zcopy_get_buf_complete, task); 110129784f35SKrzysztof Karas if (rc != 0) { 110229784f35SKrzysztof Karas assert(rc == -ENOMEM); 110329784f35SKrzysztof Karas bdevperf_queue_io_wait_with_cb(task, bdevperf_prep_zcopy_write_task); 110429784f35SKrzysztof Karas return; 110529784f35SKrzysztof Karas } 110629784f35SKrzysztof Karas 110729784f35SKrzysztof Karas job->current_queue_depth++; 110829784f35SKrzysztof Karas } 110929784f35SKrzysztof Karas 111029784f35SKrzysztof Karas static struct bdevperf_task * 111129784f35SKrzysztof Karas bdevperf_job_get_task(struct bdevperf_job *job) 111229784f35SKrzysztof Karas { 111329784f35SKrzysztof Karas struct bdevperf_task *task; 111429784f35SKrzysztof Karas 111529784f35SKrzysztof Karas task = TAILQ_FIRST(&job->task_list); 111629784f35SKrzysztof Karas if (!task) { 111729784f35SKrzysztof Karas printf("Task allocation failed\n"); 111829784f35SKrzysztof Karas abort(); 111929784f35SKrzysztof Karas } 112029784f35SKrzysztof Karas 112129784f35SKrzysztof Karas TAILQ_REMOVE(&job->task_list, task, link); 112229784f35SKrzysztof Karas return task; 112329784f35SKrzysztof Karas } 112429784f35SKrzysztof Karas 112529784f35SKrzysztof Karas static void 112629784f35SKrzysztof Karas bdevperf_submit_single(struct bdevperf_job *job, struct bdevperf_task *task) 112729784f35SKrzysztof Karas { 112829784f35SKrzysztof Karas uint64_t offset_in_ios; 11290d11cf93SJim Harris uint64_t rand_value; 1130d254a3b9SSlawomir Ptak uint32_t first_clear; 113129784f35SKrzysztof Karas 113229784f35SKrzysztof Karas if (job->zipf) { 113329784f35SKrzysztof Karas offset_in_ios = spdk_zipf_generate(job->zipf); 113429784f35SKrzysztof Karas } else if (job->is_random) { 11350d11cf93SJim Harris /* RAND_MAX is only INT32_MAX, so use 2 calls to rand_r to 11360d11cf93SJim Harris * get a large enough value to ensure we are issuing I/O 11370d11cf93SJim Harris * uniformly across the whole bdev. 11380d11cf93SJim Harris */ 11390d11cf93SJim Harris rand_value = (uint64_t)rand_r(&job->seed) * RAND_MAX + rand_r(&job->seed); 11400d11cf93SJim Harris offset_in_ios = rand_value % job->size_in_ios; 1141d254a3b9SSlawomir Ptak 1142d254a3b9SSlawomir Ptak if (g_random_map) { 1143d254a3b9SSlawomir Ptak /* Make sure, that the offset does not exceed the maximum size 1144d254a3b9SSlawomir Ptak * of the bit array (verified during job creation) 1145d254a3b9SSlawomir Ptak */ 1146d254a3b9SSlawomir Ptak assert(offset_in_ios < UINT32_MAX); 1147d254a3b9SSlawomir Ptak 1148d254a3b9SSlawomir Ptak first_clear = spdk_bit_array_find_first_clear(job->random_map, (uint32_t)offset_in_ios); 1149d254a3b9SSlawomir Ptak 1150d254a3b9SSlawomir Ptak if (first_clear == UINT32_MAX) { 1151d254a3b9SSlawomir Ptak first_clear = spdk_bit_array_find_first_clear(job->random_map, 0); 1152d254a3b9SSlawomir Ptak 1153d254a3b9SSlawomir Ptak if (first_clear == UINT32_MAX) { 1154d254a3b9SSlawomir Ptak /* If there are no more clear bits in the array, we start over 1155d254a3b9SSlawomir Ptak * and select the previously selected random value. 1156d254a3b9SSlawomir Ptak */ 1157d254a3b9SSlawomir Ptak spdk_bit_array_clear_mask(job->random_map); 1158d254a3b9SSlawomir Ptak first_clear = (uint32_t)offset_in_ios; 1159d254a3b9SSlawomir Ptak } 1160d254a3b9SSlawomir Ptak } 1161d254a3b9SSlawomir Ptak 1162d254a3b9SSlawomir Ptak spdk_bit_array_set(job->random_map, first_clear); 1163d254a3b9SSlawomir Ptak 1164d254a3b9SSlawomir Ptak offset_in_ios = first_clear; 1165d254a3b9SSlawomir Ptak } 116629784f35SKrzysztof Karas } else { 116729784f35SKrzysztof Karas offset_in_ios = job->offset_in_ios++; 116829784f35SKrzysztof Karas if (job->offset_in_ios == job->size_in_ios) { 116929784f35SKrzysztof Karas job->offset_in_ios = 0; 117029784f35SKrzysztof Karas } 117129784f35SKrzysztof Karas 117229784f35SKrzysztof Karas /* Increment of offset_in_ios if there's already an outstanding IO 117329784f35SKrzysztof Karas * to that location. We only need this with job->verify as random 117429784f35SKrzysztof Karas * offsets are not supported with job->verify at this time. 117529784f35SKrzysztof Karas */ 117629784f35SKrzysztof Karas if (job->verify) { 117729784f35SKrzysztof Karas assert(spdk_bit_array_find_first_clear(job->outstanding, 0) != UINT32_MAX); 117829784f35SKrzysztof Karas 117929784f35SKrzysztof Karas while (spdk_bit_array_get(job->outstanding, offset_in_ios)) { 118029784f35SKrzysztof Karas offset_in_ios = job->offset_in_ios++; 118129784f35SKrzysztof Karas if (job->offset_in_ios == job->size_in_ios) { 118229784f35SKrzysztof Karas job->offset_in_ios = 0; 118329784f35SKrzysztof Karas } 118429784f35SKrzysztof Karas } 118529784f35SKrzysztof Karas spdk_bit_array_set(job->outstanding, offset_in_ios); 118629784f35SKrzysztof Karas } 118729784f35SKrzysztof Karas } 118829784f35SKrzysztof Karas 118929784f35SKrzysztof Karas /* For multi-thread to same job, offset_in_ios is relative 119029784f35SKrzysztof Karas * to the LBA range assigned for that job. job->offset_blocks 119129784f35SKrzysztof Karas * is absolute (entire bdev LBA range). 119229784f35SKrzysztof Karas */ 119329784f35SKrzysztof Karas task->offset_blocks = (offset_in_ios + job->ios_base) * job->io_size_blocks; 119429784f35SKrzysztof Karas 119529784f35SKrzysztof Karas if (job->verify || job->reset) { 119629784f35SKrzysztof Karas generate_data(task->buf, job->buf_size, 119729784f35SKrzysztof Karas spdk_bdev_get_block_size(job->bdev), 119829784f35SKrzysztof Karas task->md_buf, spdk_bdev_get_md_size(job->bdev), 119929784f35SKrzysztof Karas job->io_size_blocks); 120029784f35SKrzysztof Karas if (g_zcopy) { 120129784f35SKrzysztof Karas bdevperf_prep_zcopy_write_task(task); 120229784f35SKrzysztof Karas return; 120329784f35SKrzysztof Karas } else { 120429784f35SKrzysztof Karas task->iov.iov_base = task->buf; 120529784f35SKrzysztof Karas task->iov.iov_len = job->buf_size; 120629784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_WRITE; 120729784f35SKrzysztof Karas } 120829784f35SKrzysztof Karas } else if (job->flush) { 120929784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_FLUSH; 121029784f35SKrzysztof Karas } else if (job->unmap) { 121129784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_UNMAP; 121229784f35SKrzysztof Karas } else if (job->write_zeroes) { 121329784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_WRITE_ZEROES; 121429784f35SKrzysztof Karas } else if ((job->rw_percentage == 100) || 121529784f35SKrzysztof Karas (job->rw_percentage != 0 && ((rand_r(&job->seed) % 100) < job->rw_percentage))) { 121629784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_READ; 121729784f35SKrzysztof Karas } else { 121829784f35SKrzysztof Karas if (g_zcopy) { 121929784f35SKrzysztof Karas bdevperf_prep_zcopy_write_task(task); 122029784f35SKrzysztof Karas return; 122129784f35SKrzysztof Karas } else { 122229784f35SKrzysztof Karas task->iov.iov_base = task->buf; 122329784f35SKrzysztof Karas task->iov.iov_len = job->buf_size; 122429784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_WRITE; 122529784f35SKrzysztof Karas } 122629784f35SKrzysztof Karas } 122729784f35SKrzysztof Karas 122829784f35SKrzysztof Karas bdevperf_submit_task(task); 122929784f35SKrzysztof Karas } 123029784f35SKrzysztof Karas 123129784f35SKrzysztof Karas static int reset_job(void *arg); 123229784f35SKrzysztof Karas 123329784f35SKrzysztof Karas static void 123429784f35SKrzysztof Karas reset_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 123529784f35SKrzysztof Karas { 123629784f35SKrzysztof Karas struct bdevperf_task *task = cb_arg; 123729784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 123829784f35SKrzysztof Karas 123929784f35SKrzysztof Karas if (!success) { 124029784f35SKrzysztof Karas printf("Reset blockdev=%s failed\n", spdk_bdev_get_name(job->bdev)); 124129784f35SKrzysztof Karas bdevperf_job_drain(job); 124229784f35SKrzysztof Karas g_run_rc = -1; 124329784f35SKrzysztof Karas } 124429784f35SKrzysztof Karas 124529784f35SKrzysztof Karas TAILQ_INSERT_TAIL(&job->task_list, task, link); 124629784f35SKrzysztof Karas spdk_bdev_free_io(bdev_io); 124729784f35SKrzysztof Karas 124829784f35SKrzysztof Karas job->reset_timer = SPDK_POLLER_REGISTER(reset_job, job, 1249045c781dSRichael Zhuang 10 * SPDK_SEC_TO_USEC); 125029784f35SKrzysztof Karas } 125129784f35SKrzysztof Karas 125229784f35SKrzysztof Karas static int 125329784f35SKrzysztof Karas reset_job(void *arg) 125429784f35SKrzysztof Karas { 125529784f35SKrzysztof Karas struct bdevperf_job *job = arg; 125629784f35SKrzysztof Karas struct bdevperf_task *task; 125729784f35SKrzysztof Karas int rc; 125829784f35SKrzysztof Karas 125929784f35SKrzysztof Karas spdk_poller_unregister(&job->reset_timer); 126029784f35SKrzysztof Karas 126129784f35SKrzysztof Karas /* Do reset. */ 126229784f35SKrzysztof Karas task = bdevperf_job_get_task(job); 126329784f35SKrzysztof Karas rc = spdk_bdev_reset(job->bdev_desc, job->ch, 126429784f35SKrzysztof Karas reset_cb, task); 126529784f35SKrzysztof Karas if (rc) { 126629784f35SKrzysztof Karas printf("Reset failed: %d\n", rc); 126729784f35SKrzysztof Karas bdevperf_job_drain(job); 126829784f35SKrzysztof Karas g_run_rc = -1; 126929784f35SKrzysztof Karas } 127029784f35SKrzysztof Karas 127129784f35SKrzysztof Karas return -1; 127229784f35SKrzysztof Karas } 127329784f35SKrzysztof Karas 127429784f35SKrzysztof Karas static void 127529784f35SKrzysztof Karas bdevperf_timeout_cb(void *cb_arg, struct spdk_bdev_io *bdev_io) 127629784f35SKrzysztof Karas { 127729784f35SKrzysztof Karas struct bdevperf_job *job = cb_arg; 127829784f35SKrzysztof Karas struct bdevperf_task *task; 127929784f35SKrzysztof Karas 128029784f35SKrzysztof Karas job->io_timeout++; 128129784f35SKrzysztof Karas 128229784f35SKrzysztof Karas if (job->is_draining || !job->abort || 128329784f35SKrzysztof Karas !spdk_bdev_io_type_supported(job->bdev, SPDK_BDEV_IO_TYPE_ABORT)) { 128429784f35SKrzysztof Karas return; 128529784f35SKrzysztof Karas } 128629784f35SKrzysztof Karas 128729784f35SKrzysztof Karas task = bdevperf_job_get_task(job); 128829784f35SKrzysztof Karas if (task == NULL) { 128929784f35SKrzysztof Karas return; 129029784f35SKrzysztof Karas } 129129784f35SKrzysztof Karas 129229784f35SKrzysztof Karas task->task_to_abort = spdk_bdev_io_get_cb_arg(bdev_io); 129329784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_ABORT; 129429784f35SKrzysztof Karas 129529784f35SKrzysztof Karas bdevperf_submit_task(task); 129629784f35SKrzysztof Karas } 129729784f35SKrzysztof Karas 129829784f35SKrzysztof Karas static void 129929784f35SKrzysztof Karas bdevperf_job_run(void *ctx) 130029784f35SKrzysztof Karas { 130129784f35SKrzysztof Karas struct bdevperf_job *job = ctx; 130229784f35SKrzysztof Karas struct bdevperf_task *task; 130329784f35SKrzysztof Karas int i; 130429784f35SKrzysztof Karas 130529784f35SKrzysztof Karas /* Submit initial I/O for this job. Each time one 130629784f35SKrzysztof Karas * completes, another will be submitted. */ 130729784f35SKrzysztof Karas 130829784f35SKrzysztof Karas /* Start a timer to stop this I/O chain when the run is over */ 1309893aaaccSAlexey Marchuk job->run_timer = SPDK_POLLER_REGISTER(bdevperf_job_drain_timer, job, g_time_in_usec); 131029784f35SKrzysztof Karas if (job->reset) { 131129784f35SKrzysztof Karas job->reset_timer = SPDK_POLLER_REGISTER(reset_job, job, 1312045c781dSRichael Zhuang 10 * SPDK_SEC_TO_USEC); 131329784f35SKrzysztof Karas } 131429784f35SKrzysztof Karas 131529784f35SKrzysztof Karas spdk_bdev_set_timeout(job->bdev_desc, g_timeout_in_sec, bdevperf_timeout_cb, job); 131629784f35SKrzysztof Karas 131729784f35SKrzysztof Karas for (i = 0; i < job->queue_depth; i++) { 131829784f35SKrzysztof Karas task = bdevperf_job_get_task(job); 131929784f35SKrzysztof Karas bdevperf_submit_single(job, task); 132029784f35SKrzysztof Karas } 132129784f35SKrzysztof Karas } 132229784f35SKrzysztof Karas 132329784f35SKrzysztof Karas static void 132429784f35SKrzysztof Karas _performance_dump_done(void *ctx) 132529784f35SKrzysztof Karas { 132629784f35SKrzysztof Karas struct bdevperf_aggregate_stats *stats = ctx; 1327723dd06eSRichael Zhuang double average_latency; 132829784f35SKrzysztof Karas 1329723dd06eSRichael Zhuang printf("\r ==================================================================================" 1330723dd06eSRichael Zhuang "=================================\n"); 133129784f35SKrzysztof Karas printf("\r %-28s: %10s %10.2f %10.2f", 133229784f35SKrzysztof Karas "Total", "", stats->total_io_per_second, stats->total_mb_per_second); 1333723dd06eSRichael Zhuang printf(" %10.2f %8.2f", 133429784f35SKrzysztof Karas stats->total_failed_per_second, stats->total_timeout_per_second); 1335723dd06eSRichael Zhuang 1336045c781dSRichael Zhuang average_latency = ((double)stats->total_tsc / stats->total_io_completed) * SPDK_SEC_TO_USEC / 1337723dd06eSRichael Zhuang spdk_get_ticks_hz(); 1338723dd06eSRichael Zhuang printf(" %10.2f %10.2f %10.2f\n", average_latency, stats->min_latency, stats->max_latency); 1339723dd06eSRichael Zhuang printf("\n"); 1340723dd06eSRichael Zhuang 134129784f35SKrzysztof Karas fflush(stdout); 134229784f35SKrzysztof Karas 134329784f35SKrzysztof Karas g_performance_dump_active = false; 134429784f35SKrzysztof Karas 134529784f35SKrzysztof Karas free(stats); 134629784f35SKrzysztof Karas } 134729784f35SKrzysztof Karas 134829784f35SKrzysztof Karas static void 134929784f35SKrzysztof Karas _performance_dump(void *ctx) 135029784f35SKrzysztof Karas { 135129784f35SKrzysztof Karas struct bdevperf_aggregate_stats *stats = ctx; 135229784f35SKrzysztof Karas 135329784f35SKrzysztof Karas performance_dump_job(stats, stats->current_job); 135429784f35SKrzysztof Karas 135529784f35SKrzysztof Karas /* This assumes the jobs list is static after start up time. 135629784f35SKrzysztof Karas * That's true right now, but if that ever changed this would need a lock. */ 135729784f35SKrzysztof Karas stats->current_job = TAILQ_NEXT(stats->current_job, link); 135829784f35SKrzysztof Karas if (stats->current_job == NULL) { 135929784f35SKrzysztof Karas spdk_thread_send_msg(g_main_thread, _performance_dump_done, stats); 136029784f35SKrzysztof Karas } else { 136129784f35SKrzysztof Karas spdk_thread_send_msg(stats->current_job->thread, _performance_dump, stats); 136229784f35SKrzysztof Karas } 136329784f35SKrzysztof Karas } 136429784f35SKrzysztof Karas 136529784f35SKrzysztof Karas static int 136629784f35SKrzysztof Karas performance_statistics_thread(void *arg) 136729784f35SKrzysztof Karas { 136829784f35SKrzysztof Karas struct bdevperf_aggregate_stats *stats; 136929784f35SKrzysztof Karas 137029784f35SKrzysztof Karas if (g_performance_dump_active) { 137129784f35SKrzysztof Karas return -1; 137229784f35SKrzysztof Karas } 137329784f35SKrzysztof Karas 137429784f35SKrzysztof Karas g_performance_dump_active = true; 137529784f35SKrzysztof Karas 137629784f35SKrzysztof Karas stats = calloc(1, sizeof(*stats)); 137729784f35SKrzysztof Karas if (stats == NULL) { 137829784f35SKrzysztof Karas return -1; 137929784f35SKrzysztof Karas } 138029784f35SKrzysztof Karas 1381723dd06eSRichael Zhuang stats->min_latency = (double)UINT64_MAX; 1382723dd06eSRichael Zhuang 138329784f35SKrzysztof Karas g_show_performance_period_num++; 138429784f35SKrzysztof Karas 138529784f35SKrzysztof Karas stats->io_time_in_usec = g_show_performance_period_num * g_show_performance_period_in_usec; 138629784f35SKrzysztof Karas stats->ema_period = g_show_performance_ema_period; 138729784f35SKrzysztof Karas 138829784f35SKrzysztof Karas /* Iterate all of the jobs to gather stats 138929784f35SKrzysztof Karas * These jobs will not get removed here until a final performance dump is run, 139029784f35SKrzysztof Karas * so this should be safe without locking. 139129784f35SKrzysztof Karas */ 139229784f35SKrzysztof Karas stats->current_job = TAILQ_FIRST(&g_bdevperf.jobs); 139329784f35SKrzysztof Karas if (stats->current_job == NULL) { 139429784f35SKrzysztof Karas spdk_thread_send_msg(g_main_thread, _performance_dump_done, stats); 139529784f35SKrzysztof Karas } else { 139629784f35SKrzysztof Karas spdk_thread_send_msg(stats->current_job->thread, _performance_dump, stats); 139729784f35SKrzysztof Karas } 139829784f35SKrzysztof Karas 139929784f35SKrzysztof Karas return -1; 140029784f35SKrzysztof Karas } 140129784f35SKrzysztof Karas 140229784f35SKrzysztof Karas static void 140329784f35SKrzysztof Karas bdevperf_test(void) 140429784f35SKrzysztof Karas { 140529784f35SKrzysztof Karas struct bdevperf_job *job; 140629784f35SKrzysztof Karas 1407045c781dSRichael Zhuang printf("Running I/O for %" PRIu64 " seconds...\n", g_time_in_usec / (uint64_t)SPDK_SEC_TO_USEC); 140829784f35SKrzysztof Karas fflush(stdout); 140929784f35SKrzysztof Karas 141029784f35SKrzysztof Karas /* Start a timer to dump performance numbers */ 141129784f35SKrzysztof Karas g_start_tsc = spdk_get_ticks(); 141229784f35SKrzysztof Karas if (g_show_performance_real_time && !g_perf_timer) { 1413723dd06eSRichael Zhuang printf("%*s\n", 107, "Latency(us)"); 1414723dd06eSRichael Zhuang printf("\r %-*s: %10s %10s %10s %10s %8s %10s %10s %10s\n", 1415723dd06eSRichael Zhuang 28, "Device Information", "runtime(s)", "IOPS", "MiB/s", "Fail/s", "TO/s", "Average", "min", "max"); 141629784f35SKrzysztof Karas 141729784f35SKrzysztof Karas g_perf_timer = SPDK_POLLER_REGISTER(performance_statistics_thread, NULL, 141829784f35SKrzysztof Karas g_show_performance_period_in_usec); 141929784f35SKrzysztof Karas } 142029784f35SKrzysztof Karas 142129784f35SKrzysztof Karas /* Iterate jobs to start all I/O */ 142229784f35SKrzysztof Karas TAILQ_FOREACH(job, &g_bdevperf.jobs, link) { 142329784f35SKrzysztof Karas g_bdevperf.running_jobs++; 142429784f35SKrzysztof Karas spdk_thread_send_msg(job->thread, bdevperf_job_run, job); 142529784f35SKrzysztof Karas } 142629784f35SKrzysztof Karas } 142729784f35SKrzysztof Karas 142829784f35SKrzysztof Karas static void 142929784f35SKrzysztof Karas bdevperf_bdev_removed(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx) 143029784f35SKrzysztof Karas { 143129784f35SKrzysztof Karas struct bdevperf_job *job = event_ctx; 143229784f35SKrzysztof Karas 143329784f35SKrzysztof Karas if (SPDK_BDEV_EVENT_REMOVE == type) { 143429784f35SKrzysztof Karas bdevperf_job_drain(job); 143529784f35SKrzysztof Karas } 143629784f35SKrzysztof Karas } 143729784f35SKrzysztof Karas 1438723dd06eSRichael Zhuang static void 1439723dd06eSRichael Zhuang bdevperf_histogram_status_cb(void *cb_arg, int status) 1440723dd06eSRichael Zhuang { 1441723dd06eSRichael Zhuang if (status != 0) { 1442723dd06eSRichael Zhuang g_run_rc = status; 1443723dd06eSRichael Zhuang if (g_continue_on_failure == false) { 1444723dd06eSRichael Zhuang g_error_to_exit = true; 1445723dd06eSRichael Zhuang } 1446723dd06eSRichael Zhuang } 1447723dd06eSRichael Zhuang 1448723dd06eSRichael Zhuang if (--g_bdev_count == 0) { 1449723dd06eSRichael Zhuang if (g_run_rc == 0) { 1450723dd06eSRichael Zhuang /* Ready to run the test */ 1451723dd06eSRichael Zhuang bdevperf_test(); 1452723dd06eSRichael Zhuang } else { 1453723dd06eSRichael Zhuang bdevperf_test_done(NULL); 1454723dd06eSRichael Zhuang } 1455723dd06eSRichael Zhuang } 1456723dd06eSRichael Zhuang } 1457723dd06eSRichael Zhuang 145829784f35SKrzysztof Karas static uint32_t g_construct_job_count = 0; 145929784f35SKrzysztof Karas 146056479486SShuhei Matsumoto static int 146156479486SShuhei Matsumoto _bdevperf_enable_histogram(void *ctx, struct spdk_bdev *bdev) 146256479486SShuhei Matsumoto { 146356479486SShuhei Matsumoto bool *enable = ctx; 146456479486SShuhei Matsumoto 146556479486SShuhei Matsumoto g_bdev_count++; 146656479486SShuhei Matsumoto 146756479486SShuhei Matsumoto spdk_bdev_histogram_enable(bdev, bdevperf_histogram_status_cb, NULL, *enable); 146856479486SShuhei Matsumoto 146956479486SShuhei Matsumoto return 0; 147056479486SShuhei Matsumoto } 147156479486SShuhei Matsumoto 147229784f35SKrzysztof Karas static void 147356479486SShuhei Matsumoto bdevperf_enable_histogram(bool enable) 1474723dd06eSRichael Zhuang { 1475723dd06eSRichael Zhuang struct spdk_bdev *bdev; 147656479486SShuhei Matsumoto int rc; 147756479486SShuhei Matsumoto 1478723dd06eSRichael Zhuang /* increment initial g_bdev_count so that it will never reach 0 in the middle of iteration */ 1479723dd06eSRichael Zhuang g_bdev_count = 1; 1480723dd06eSRichael Zhuang 1481723dd06eSRichael Zhuang if (g_job_bdev_name != NULL) { 1482723dd06eSRichael Zhuang bdev = spdk_bdev_get_by_name(g_job_bdev_name); 1483723dd06eSRichael Zhuang if (bdev) { 148456479486SShuhei Matsumoto rc = _bdevperf_enable_histogram(&enable, bdev); 1485723dd06eSRichael Zhuang } else { 1486723dd06eSRichael Zhuang fprintf(stderr, "Unable to find bdev '%s'\n", g_job_bdev_name); 148756479486SShuhei Matsumoto rc = -1; 1488723dd06eSRichael Zhuang } 1489723dd06eSRichael Zhuang } else { 149056479486SShuhei Matsumoto rc = spdk_for_each_bdev_leaf(&enable, _bdevperf_enable_histogram); 1491723dd06eSRichael Zhuang } 1492723dd06eSRichael Zhuang 149356479486SShuhei Matsumoto bdevperf_histogram_status_cb(NULL, rc); 1494723dd06eSRichael Zhuang } 1495723dd06eSRichael Zhuang 1496723dd06eSRichael Zhuang static void 149729784f35SKrzysztof Karas _bdevperf_construct_job_done(void *ctx) 149829784f35SKrzysztof Karas { 149929784f35SKrzysztof Karas if (--g_construct_job_count == 0) { 150029784f35SKrzysztof Karas if (g_run_rc != 0) { 150129784f35SKrzysztof Karas /* Something failed. */ 150229784f35SKrzysztof Karas bdevperf_test_done(NULL); 150329784f35SKrzysztof Karas return; 150429784f35SKrzysztof Karas } 150529784f35SKrzysztof Karas 1506723dd06eSRichael Zhuang /* always enable histogram. */ 150756479486SShuhei Matsumoto bdevperf_enable_histogram(true); 150829784f35SKrzysztof Karas } else if (g_run_rc != 0) { 150929784f35SKrzysztof Karas /* Reset error as some jobs constructed right */ 151029784f35SKrzysztof Karas g_run_rc = 0; 151129784f35SKrzysztof Karas if (g_continue_on_failure == false) { 151229784f35SKrzysztof Karas g_error_to_exit = true; 151329784f35SKrzysztof Karas } 151429784f35SKrzysztof Karas } 151529784f35SKrzysztof Karas } 151629784f35SKrzysztof Karas 151729784f35SKrzysztof Karas /* Checkformat will not allow to use inlined type, 151829784f35SKrzysztof Karas this is a workaround */ 151929784f35SKrzysztof Karas typedef struct spdk_thread *spdk_thread_t; 152029784f35SKrzysztof Karas 152129784f35SKrzysztof Karas static spdk_thread_t 152229784f35SKrzysztof Karas construct_job_thread(struct spdk_cpuset *cpumask, const char *tag) 152329784f35SKrzysztof Karas { 152429784f35SKrzysztof Karas struct spdk_cpuset tmp; 152529784f35SKrzysztof Karas 152629784f35SKrzysztof Karas /* This function runs on the main thread. */ 152729784f35SKrzysztof Karas assert(g_main_thread == spdk_get_thread()); 152829784f35SKrzysztof Karas 152929784f35SKrzysztof Karas /* Handle default mask */ 153029784f35SKrzysztof Karas if (spdk_cpuset_count(cpumask) == 0) { 153129784f35SKrzysztof Karas cpumask = &g_all_cpuset; 153229784f35SKrzysztof Karas } 153329784f35SKrzysztof Karas 153429784f35SKrzysztof Karas /* Warn user that mask might need to be changed */ 153529784f35SKrzysztof Karas spdk_cpuset_copy(&tmp, cpumask); 153629784f35SKrzysztof Karas spdk_cpuset_or(&tmp, &g_all_cpuset); 153729784f35SKrzysztof Karas if (!spdk_cpuset_equal(&tmp, &g_all_cpuset)) { 153829784f35SKrzysztof Karas fprintf(stderr, "cpumask for '%s' is too big\n", tag); 153929784f35SKrzysztof Karas } 154029784f35SKrzysztof Karas 154129784f35SKrzysztof Karas return spdk_thread_create(tag, cpumask); 154229784f35SKrzysztof Karas } 154329784f35SKrzysztof Karas 154429784f35SKrzysztof Karas static uint32_t 154529784f35SKrzysztof Karas _get_next_core(void) 154629784f35SKrzysztof Karas { 154729784f35SKrzysztof Karas static uint32_t current_core = SPDK_ENV_LCORE_ID_ANY; 154829784f35SKrzysztof Karas 154929784f35SKrzysztof Karas if (current_core == SPDK_ENV_LCORE_ID_ANY) { 155029784f35SKrzysztof Karas current_core = spdk_env_get_first_core(); 155129784f35SKrzysztof Karas return current_core; 155229784f35SKrzysztof Karas } 155329784f35SKrzysztof Karas 155429784f35SKrzysztof Karas current_core = spdk_env_get_next_core(current_core); 155529784f35SKrzysztof Karas if (current_core == SPDK_ENV_LCORE_ID_ANY) { 155629784f35SKrzysztof Karas current_core = spdk_env_get_first_core(); 155729784f35SKrzysztof Karas } 155829784f35SKrzysztof Karas 155929784f35SKrzysztof Karas return current_core; 156029784f35SKrzysztof Karas } 156129784f35SKrzysztof Karas 156229784f35SKrzysztof Karas static void 156329784f35SKrzysztof Karas _bdevperf_construct_job(void *ctx) 156429784f35SKrzysztof Karas { 156529784f35SKrzysztof Karas struct bdevperf_job *job = ctx; 156629784f35SKrzysztof Karas int rc; 156729784f35SKrzysztof Karas 156829784f35SKrzysztof Karas rc = spdk_bdev_open_ext(spdk_bdev_get_name(job->bdev), true, bdevperf_bdev_removed, job, 156929784f35SKrzysztof Karas &job->bdev_desc); 157029784f35SKrzysztof Karas if (rc != 0) { 157129784f35SKrzysztof Karas SPDK_ERRLOG("Could not open leaf bdev %s, error=%d\n", spdk_bdev_get_name(job->bdev), rc); 157229784f35SKrzysztof Karas g_run_rc = -EINVAL; 157329784f35SKrzysztof Karas goto end; 157429784f35SKrzysztof Karas } 157529784f35SKrzysztof Karas 157629784f35SKrzysztof Karas if (g_zcopy) { 157729784f35SKrzysztof Karas if (!spdk_bdev_io_type_supported(job->bdev, SPDK_BDEV_IO_TYPE_ZCOPY)) { 157829784f35SKrzysztof Karas printf("Test requires ZCOPY but bdev module does not support ZCOPY\n"); 157929784f35SKrzysztof Karas g_run_rc = -ENOTSUP; 158029784f35SKrzysztof Karas goto end; 158129784f35SKrzysztof Karas } 158229784f35SKrzysztof Karas } 158329784f35SKrzysztof Karas 158429784f35SKrzysztof Karas job->ch = spdk_bdev_get_io_channel(job->bdev_desc); 158529784f35SKrzysztof Karas if (!job->ch) { 158629784f35SKrzysztof Karas SPDK_ERRLOG("Could not get io_channel for device %s, error=%d\n", spdk_bdev_get_name(job->bdev), 158729784f35SKrzysztof Karas rc); 158829784f35SKrzysztof Karas spdk_bdev_close(job->bdev_desc); 158929784f35SKrzysztof Karas TAILQ_REMOVE(&g_bdevperf.jobs, job, link); 159029784f35SKrzysztof Karas g_run_rc = -ENOMEM; 159129784f35SKrzysztof Karas goto end; 159229784f35SKrzysztof Karas } 159329784f35SKrzysztof Karas 159429784f35SKrzysztof Karas end: 159529784f35SKrzysztof Karas spdk_thread_send_msg(g_main_thread, _bdevperf_construct_job_done, NULL); 159629784f35SKrzysztof Karas } 159729784f35SKrzysztof Karas 159829784f35SKrzysztof Karas static void 159929784f35SKrzysztof Karas job_init_rw(struct bdevperf_job *job, enum job_config_rw rw) 160029784f35SKrzysztof Karas { 160129784f35SKrzysztof Karas switch (rw) { 160229784f35SKrzysztof Karas case JOB_CONFIG_RW_READ: 160329784f35SKrzysztof Karas job->rw_percentage = 100; 160429784f35SKrzysztof Karas break; 160529784f35SKrzysztof Karas case JOB_CONFIG_RW_WRITE: 160629784f35SKrzysztof Karas job->rw_percentage = 0; 160729784f35SKrzysztof Karas break; 160829784f35SKrzysztof Karas case JOB_CONFIG_RW_RANDREAD: 160929784f35SKrzysztof Karas job->is_random = true; 161029784f35SKrzysztof Karas job->rw_percentage = 100; 161129784f35SKrzysztof Karas job->seed = rand(); 161229784f35SKrzysztof Karas break; 161329784f35SKrzysztof Karas case JOB_CONFIG_RW_RANDWRITE: 161429784f35SKrzysztof Karas job->is_random = true; 161529784f35SKrzysztof Karas job->rw_percentage = 0; 161629784f35SKrzysztof Karas job->seed = rand(); 161729784f35SKrzysztof Karas break; 161829784f35SKrzysztof Karas case JOB_CONFIG_RW_RW: 161929784f35SKrzysztof Karas job->is_random = false; 162029784f35SKrzysztof Karas break; 162129784f35SKrzysztof Karas case JOB_CONFIG_RW_RANDRW: 162229784f35SKrzysztof Karas job->is_random = true; 162329784f35SKrzysztof Karas job->seed = rand(); 162429784f35SKrzysztof Karas break; 162529784f35SKrzysztof Karas case JOB_CONFIG_RW_VERIFY: 162629784f35SKrzysztof Karas job->verify = true; 162729784f35SKrzysztof Karas job->rw_percentage = 50; 162829784f35SKrzysztof Karas break; 162929784f35SKrzysztof Karas case JOB_CONFIG_RW_RESET: 163029784f35SKrzysztof Karas job->reset = true; 163129784f35SKrzysztof Karas job->verify = true; 163229784f35SKrzysztof Karas job->rw_percentage = 50; 163329784f35SKrzysztof Karas break; 163429784f35SKrzysztof Karas case JOB_CONFIG_RW_UNMAP: 163529784f35SKrzysztof Karas job->unmap = true; 163629784f35SKrzysztof Karas break; 163729784f35SKrzysztof Karas case JOB_CONFIG_RW_FLUSH: 163829784f35SKrzysztof Karas job->flush = true; 163929784f35SKrzysztof Karas break; 164029784f35SKrzysztof Karas case JOB_CONFIG_RW_WRITE_ZEROES: 164129784f35SKrzysztof Karas job->write_zeroes = true; 164229784f35SKrzysztof Karas break; 164329784f35SKrzysztof Karas } 164429784f35SKrzysztof Karas } 164529784f35SKrzysztof Karas 164629784f35SKrzysztof Karas static int 164729784f35SKrzysztof Karas bdevperf_construct_job(struct spdk_bdev *bdev, struct job_config *config, 164829784f35SKrzysztof Karas struct spdk_thread *thread) 164929784f35SKrzysztof Karas { 165029784f35SKrzysztof Karas struct bdevperf_job *job; 165129784f35SKrzysztof Karas struct bdevperf_task *task; 165229784f35SKrzysztof Karas int block_size, data_block_size; 165329784f35SKrzysztof Karas int rc; 165429784f35SKrzysztof Karas int task_num, n; 165529784f35SKrzysztof Karas 165629784f35SKrzysztof Karas block_size = spdk_bdev_get_block_size(bdev); 165729784f35SKrzysztof Karas data_block_size = spdk_bdev_get_data_block_size(bdev); 165829784f35SKrzysztof Karas 165929784f35SKrzysztof Karas job = calloc(1, sizeof(struct bdevperf_job)); 166029784f35SKrzysztof Karas if (!job) { 166129784f35SKrzysztof Karas fprintf(stderr, "Unable to allocate memory for new job.\n"); 166229784f35SKrzysztof Karas return -ENOMEM; 166329784f35SKrzysztof Karas } 166429784f35SKrzysztof Karas 166529784f35SKrzysztof Karas job->name = strdup(spdk_bdev_get_name(bdev)); 166629784f35SKrzysztof Karas if (!job->name) { 166729784f35SKrzysztof Karas fprintf(stderr, "Unable to allocate memory for job name.\n"); 1668dcb296a3SJim Harris bdevperf_job_free(job); 166929784f35SKrzysztof Karas return -ENOMEM; 167029784f35SKrzysztof Karas } 167129784f35SKrzysztof Karas 167229784f35SKrzysztof Karas job->workload_type = g_workload_type; 167329784f35SKrzysztof Karas job->io_size = config->bs; 167429784f35SKrzysztof Karas job->rw_percentage = config->rwmixread; 167529784f35SKrzysztof Karas job->continue_on_failure = g_continue_on_failure; 167629784f35SKrzysztof Karas job->queue_depth = config->iodepth; 167729784f35SKrzysztof Karas job->bdev = bdev; 167829784f35SKrzysztof Karas job->io_size_blocks = job->io_size / data_block_size; 167929784f35SKrzysztof Karas job->buf_size = job->io_size_blocks * block_size; 168029784f35SKrzysztof Karas job->abort = g_abort; 168129784f35SKrzysztof Karas job_init_rw(job, config->rw); 168229784f35SKrzysztof Karas 168329784f35SKrzysztof Karas if ((job->io_size % data_block_size) != 0) { 168429784f35SKrzysztof Karas SPDK_ERRLOG("IO size (%d) is not multiples of data block size of bdev %s (%"PRIu32")\n", 168529784f35SKrzysztof Karas job->io_size, spdk_bdev_get_name(bdev), data_block_size); 1686dcb296a3SJim Harris bdevperf_job_free(job); 168729784f35SKrzysztof Karas return -ENOTSUP; 168829784f35SKrzysztof Karas } 168929784f35SKrzysztof Karas 169029784f35SKrzysztof Karas if (job->unmap && !spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP)) { 169129784f35SKrzysztof Karas printf("Skipping %s because it does not support unmap\n", spdk_bdev_get_name(bdev)); 1692dcb296a3SJim Harris bdevperf_job_free(job); 169329784f35SKrzysztof Karas return -ENOTSUP; 169429784f35SKrzysztof Karas } 169529784f35SKrzysztof Karas 169629784f35SKrzysztof Karas if (spdk_bdev_is_dif_check_enabled(bdev, SPDK_DIF_CHECK_TYPE_REFTAG)) { 169729784f35SKrzysztof Karas job->dif_check_flags |= SPDK_DIF_FLAGS_REFTAG_CHECK; 169829784f35SKrzysztof Karas } 169929784f35SKrzysztof Karas if (spdk_bdev_is_dif_check_enabled(bdev, SPDK_DIF_CHECK_TYPE_GUARD)) { 170029784f35SKrzysztof Karas job->dif_check_flags |= SPDK_DIF_FLAGS_GUARD_CHECK; 170129784f35SKrzysztof Karas } 170229784f35SKrzysztof Karas 170329784f35SKrzysztof Karas job->offset_in_ios = 0; 170429784f35SKrzysztof Karas 170529784f35SKrzysztof Karas if (config->length != 0) { 170629784f35SKrzysztof Karas /* Use subset of disk */ 170729784f35SKrzysztof Karas job->size_in_ios = config->length / job->io_size_blocks; 170829784f35SKrzysztof Karas job->ios_base = config->offset / job->io_size_blocks; 170929784f35SKrzysztof Karas } else { 171029784f35SKrzysztof Karas /* Use whole disk */ 171129784f35SKrzysztof Karas job->size_in_ios = spdk_bdev_get_num_blocks(bdev) / job->io_size_blocks; 171229784f35SKrzysztof Karas job->ios_base = 0; 171329784f35SKrzysztof Karas } 171429784f35SKrzysztof Karas 171529784f35SKrzysztof Karas if (job->is_random && g_zipf_theta > 0) { 171629784f35SKrzysztof Karas job->zipf = spdk_zipf_create(job->size_in_ios, g_zipf_theta, 0); 171729784f35SKrzysztof Karas } 171829784f35SKrzysztof Karas 171929784f35SKrzysztof Karas if (job->verify) { 1720652db802SSlawomir Ptak if (job->size_in_ios >= UINT32_MAX) { 1721652db802SSlawomir Ptak SPDK_ERRLOG("Due to constraints of verify operation, the job storage capacity is too large\n"); 1722652db802SSlawomir Ptak bdevperf_job_free(job); 1723652db802SSlawomir Ptak return -ENOMEM; 1724652db802SSlawomir Ptak } 172529784f35SKrzysztof Karas job->outstanding = spdk_bit_array_create(job->size_in_ios); 172629784f35SKrzysztof Karas if (job->outstanding == NULL) { 172729784f35SKrzysztof Karas SPDK_ERRLOG("Could not create outstanding array bitmap for bdev %s\n", 172829784f35SKrzysztof Karas spdk_bdev_get_name(bdev)); 1729dcb296a3SJim Harris bdevperf_job_free(job); 173029784f35SKrzysztof Karas return -ENOMEM; 173129784f35SKrzysztof Karas } 1732e58885f9SAlexey Marchuk if (job->queue_depth > (int)job->size_in_ios) { 1733e58885f9SAlexey Marchuk SPDK_WARNLOG("Due to constraints of verify job, queue depth (-q, %d) can't exceed the number of IO " 1734e58885f9SAlexey Marchuk "requests which can be submitted to the bdev %s simultaneously (%"PRIu64"). " 1735e58885f9SAlexey Marchuk "Queue depth is limited to %"PRIu64"\n", 1736e58885f9SAlexey Marchuk job->queue_depth, job->name, job->size_in_ios, job->size_in_ios); 1737e58885f9SAlexey Marchuk job->queue_depth = (int)job->size_in_ios; 1738e58885f9SAlexey Marchuk } 173929784f35SKrzysztof Karas } 174029784f35SKrzysztof Karas 1741723dd06eSRichael Zhuang job->histogram = spdk_histogram_data_alloc(); 1742723dd06eSRichael Zhuang if (job->histogram == NULL) { 1743723dd06eSRichael Zhuang fprintf(stderr, "Failed to allocate histogram\n"); 1744723dd06eSRichael Zhuang bdevperf_job_free(job); 1745723dd06eSRichael Zhuang return -ENOMEM; 1746723dd06eSRichael Zhuang } 1747723dd06eSRichael Zhuang 174829784f35SKrzysztof Karas TAILQ_INIT(&job->task_list); 174929784f35SKrzysztof Karas 1750d254a3b9SSlawomir Ptak if (g_random_map) { 1751d254a3b9SSlawomir Ptak if (job->size_in_ios >= UINT32_MAX) { 1752d254a3b9SSlawomir Ptak SPDK_ERRLOG("Due to constraints of the random map, the job storage capacity is too large\n"); 1753d254a3b9SSlawomir Ptak bdevperf_job_free(job); 1754d254a3b9SSlawomir Ptak return -ENOMEM; 1755d254a3b9SSlawomir Ptak } 1756d254a3b9SSlawomir Ptak job->random_map = spdk_bit_array_create(job->size_in_ios); 1757d254a3b9SSlawomir Ptak if (job->random_map == NULL) { 1758d254a3b9SSlawomir Ptak SPDK_ERRLOG("Could not create random_map array bitmap for bdev %s\n", 1759d254a3b9SSlawomir Ptak spdk_bdev_get_name(bdev)); 1760d254a3b9SSlawomir Ptak bdevperf_job_free(job); 1761d254a3b9SSlawomir Ptak return -ENOMEM; 1762d254a3b9SSlawomir Ptak } 1763d254a3b9SSlawomir Ptak } 1764d254a3b9SSlawomir Ptak 176529784f35SKrzysztof Karas task_num = job->queue_depth; 176629784f35SKrzysztof Karas if (job->reset) { 176729784f35SKrzysztof Karas task_num += 1; 176829784f35SKrzysztof Karas } 176929784f35SKrzysztof Karas if (job->abort) { 177029784f35SKrzysztof Karas task_num += job->queue_depth; 177129784f35SKrzysztof Karas } 177229784f35SKrzysztof Karas 177329784f35SKrzysztof Karas TAILQ_INSERT_TAIL(&g_bdevperf.jobs, job, link); 177429784f35SKrzysztof Karas 177529784f35SKrzysztof Karas for (n = 0; n < task_num; n++) { 177629784f35SKrzysztof Karas task = calloc(1, sizeof(struct bdevperf_task)); 177729784f35SKrzysztof Karas if (!task) { 177829784f35SKrzysztof Karas fprintf(stderr, "Failed to allocate task from memory\n"); 17798e8c360bSKrzysztof Karas spdk_zipf_free(&job->zipf); 178029784f35SKrzysztof Karas return -ENOMEM; 178129784f35SKrzysztof Karas } 178229784f35SKrzysztof Karas 178329784f35SKrzysztof Karas task->buf = spdk_zmalloc(job->buf_size, spdk_bdev_get_buf_align(job->bdev), NULL, 178429784f35SKrzysztof Karas SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); 178529784f35SKrzysztof Karas if (!task->buf) { 178629784f35SKrzysztof Karas fprintf(stderr, "Cannot allocate buf for task=%p\n", task); 17878e8c360bSKrzysztof Karas spdk_zipf_free(&job->zipf); 178829784f35SKrzysztof Karas free(task); 178929784f35SKrzysztof Karas return -ENOMEM; 179029784f35SKrzysztof Karas } 179129784f35SKrzysztof Karas 179229784f35SKrzysztof Karas if (spdk_bdev_is_md_separate(job->bdev)) { 179329784f35SKrzysztof Karas task->md_buf = spdk_zmalloc(job->io_size_blocks * 179429784f35SKrzysztof Karas spdk_bdev_get_md_size(job->bdev), 0, NULL, 179529784f35SKrzysztof Karas SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); 179629784f35SKrzysztof Karas if (!task->md_buf) { 179729784f35SKrzysztof Karas fprintf(stderr, "Cannot allocate md buf for task=%p\n", task); 17988e8c360bSKrzysztof Karas spdk_zipf_free(&job->zipf); 179929784f35SKrzysztof Karas spdk_free(task->buf); 180029784f35SKrzysztof Karas free(task); 180129784f35SKrzysztof Karas return -ENOMEM; 180229784f35SKrzysztof Karas } 180329784f35SKrzysztof Karas } 180429784f35SKrzysztof Karas 180529784f35SKrzysztof Karas task->job = job; 180629784f35SKrzysztof Karas TAILQ_INSERT_TAIL(&job->task_list, task, link); 180729784f35SKrzysztof Karas } 180829784f35SKrzysztof Karas 180929784f35SKrzysztof Karas job->thread = thread; 181029784f35SKrzysztof Karas 181129784f35SKrzysztof Karas g_construct_job_count++; 181229784f35SKrzysztof Karas 181329784f35SKrzysztof Karas rc = spdk_thread_send_msg(thread, _bdevperf_construct_job, job); 181429784f35SKrzysztof Karas assert(rc == 0); 181529784f35SKrzysztof Karas 181629784f35SKrzysztof Karas return rc; 181729784f35SKrzysztof Karas } 181829784f35SKrzysztof Karas 181929784f35SKrzysztof Karas static int 182029784f35SKrzysztof Karas parse_rw(const char *str, enum job_config_rw ret) 182129784f35SKrzysztof Karas { 182229784f35SKrzysztof Karas if (str == NULL) { 182329784f35SKrzysztof Karas return ret; 182429784f35SKrzysztof Karas } 182529784f35SKrzysztof Karas 182629784f35SKrzysztof Karas if (!strcmp(str, "read")) { 182729784f35SKrzysztof Karas ret = JOB_CONFIG_RW_READ; 182829784f35SKrzysztof Karas } else if (!strcmp(str, "randread")) { 182929784f35SKrzysztof Karas ret = JOB_CONFIG_RW_RANDREAD; 183029784f35SKrzysztof Karas } else if (!strcmp(str, "write")) { 183129784f35SKrzysztof Karas ret = JOB_CONFIG_RW_WRITE; 183229784f35SKrzysztof Karas } else if (!strcmp(str, "randwrite")) { 183329784f35SKrzysztof Karas ret = JOB_CONFIG_RW_RANDWRITE; 183429784f35SKrzysztof Karas } else if (!strcmp(str, "verify")) { 183529784f35SKrzysztof Karas ret = JOB_CONFIG_RW_VERIFY; 183629784f35SKrzysztof Karas } else if (!strcmp(str, "reset")) { 183729784f35SKrzysztof Karas ret = JOB_CONFIG_RW_RESET; 183829784f35SKrzysztof Karas } else if (!strcmp(str, "unmap")) { 183929784f35SKrzysztof Karas ret = JOB_CONFIG_RW_UNMAP; 184029784f35SKrzysztof Karas } else if (!strcmp(str, "write_zeroes")) { 184129784f35SKrzysztof Karas ret = JOB_CONFIG_RW_WRITE_ZEROES; 184229784f35SKrzysztof Karas } else if (!strcmp(str, "flush")) { 184329784f35SKrzysztof Karas ret = JOB_CONFIG_RW_FLUSH; 184429784f35SKrzysztof Karas } else if (!strcmp(str, "rw")) { 184529784f35SKrzysztof Karas ret = JOB_CONFIG_RW_RW; 184629784f35SKrzysztof Karas } else if (!strcmp(str, "randrw")) { 184729784f35SKrzysztof Karas ret = JOB_CONFIG_RW_RANDRW; 184829784f35SKrzysztof Karas } else { 184929784f35SKrzysztof Karas fprintf(stderr, "rw must be one of\n" 185029784f35SKrzysztof Karas "(read, write, randread, randwrite, rw, randrw, verify, reset, unmap, flush)\n"); 185129784f35SKrzysztof Karas ret = BDEVPERF_CONFIG_ERROR; 185229784f35SKrzysztof Karas } 185329784f35SKrzysztof Karas 185429784f35SKrzysztof Karas return ret; 185529784f35SKrzysztof Karas } 185629784f35SKrzysztof Karas 185729784f35SKrzysztof Karas static const char * 185829784f35SKrzysztof Karas config_filename_next(const char *filename, char *out) 185929784f35SKrzysztof Karas { 186029784f35SKrzysztof Karas int i, k; 186129784f35SKrzysztof Karas 186229784f35SKrzysztof Karas if (filename == NULL) { 186329784f35SKrzysztof Karas out[0] = '\0'; 186429784f35SKrzysztof Karas return NULL; 186529784f35SKrzysztof Karas } 186629784f35SKrzysztof Karas 186729784f35SKrzysztof Karas if (filename[0] == ':') { 186829784f35SKrzysztof Karas filename++; 186929784f35SKrzysztof Karas } 187029784f35SKrzysztof Karas 187129784f35SKrzysztof Karas for (i = 0, k = 0; 187229784f35SKrzysztof Karas filename[i] != '\0' && 187329784f35SKrzysztof Karas filename[i] != ':' && 18740a638e9dSMarcin Spiewak i < BDEVPERF_CONFIG_MAX_FILENAME && 18750a638e9dSMarcin Spiewak k < (BDEVPERF_CONFIG_MAX_FILENAME - 1); 187629784f35SKrzysztof Karas i++) { 187729784f35SKrzysztof Karas if (filename[i] == ' ' || filename[i] == '\t') { 187829784f35SKrzysztof Karas continue; 187929784f35SKrzysztof Karas } 188029784f35SKrzysztof Karas 188129784f35SKrzysztof Karas out[k++] = filename[i]; 188229784f35SKrzysztof Karas } 188329784f35SKrzysztof Karas out[k] = 0; 188429784f35SKrzysztof Karas 188529784f35SKrzysztof Karas return filename + i; 188629784f35SKrzysztof Karas } 188729784f35SKrzysztof Karas 18888e373044SShuhei Matsumoto static struct spdk_thread * 18898e373044SShuhei Matsumoto get_lcore_thread(uint32_t lcore) 18908e373044SShuhei Matsumoto { 18918e373044SShuhei Matsumoto struct lcore_thread *lthread; 18928e373044SShuhei Matsumoto 18938e373044SShuhei Matsumoto TAILQ_FOREACH(lthread, &g_lcore_thread_list, link) { 18948e373044SShuhei Matsumoto if (lthread->lcore == lcore) { 18958e373044SShuhei Matsumoto return lthread->thread; 18968e373044SShuhei Matsumoto } 18978e373044SShuhei Matsumoto } 18988e373044SShuhei Matsumoto 18998e373044SShuhei Matsumoto return NULL; 19008e373044SShuhei Matsumoto } 19018e373044SShuhei Matsumoto 190229784f35SKrzysztof Karas static void 190329784f35SKrzysztof Karas bdevperf_construct_jobs(void) 190429784f35SKrzysztof Karas { 190529784f35SKrzysztof Karas char filename[BDEVPERF_CONFIG_MAX_FILENAME]; 190629784f35SKrzysztof Karas struct spdk_thread *thread; 190729784f35SKrzysztof Karas struct job_config *config; 190829784f35SKrzysztof Karas struct spdk_bdev *bdev; 190929784f35SKrzysztof Karas const char *filenames; 191029784f35SKrzysztof Karas int rc; 191129784f35SKrzysztof Karas 191229784f35SKrzysztof Karas TAILQ_FOREACH(config, &job_config_list, link) { 191329784f35SKrzysztof Karas filenames = config->filename; 191429784f35SKrzysztof Karas 19158e373044SShuhei Matsumoto if (!g_one_thread_per_lcore) { 191629784f35SKrzysztof Karas thread = construct_job_thread(&config->cpumask, config->name); 19178e373044SShuhei Matsumoto } else { 19188e373044SShuhei Matsumoto thread = get_lcore_thread(config->lcore); 19198e373044SShuhei Matsumoto } 192029784f35SKrzysztof Karas assert(thread); 192129784f35SKrzysztof Karas 192229784f35SKrzysztof Karas while (filenames) { 192329784f35SKrzysztof Karas filenames = config_filename_next(filenames, filename); 192429784f35SKrzysztof Karas if (strlen(filename) == 0) { 192529784f35SKrzysztof Karas break; 192629784f35SKrzysztof Karas } 192729784f35SKrzysztof Karas 192829784f35SKrzysztof Karas bdev = spdk_bdev_get_by_name(filename); 192929784f35SKrzysztof Karas if (!bdev) { 193029784f35SKrzysztof Karas fprintf(stderr, "Unable to find bdev '%s'\n", filename); 193129784f35SKrzysztof Karas g_run_rc = -EINVAL; 193229784f35SKrzysztof Karas return; 193329784f35SKrzysztof Karas } 193429784f35SKrzysztof Karas 193529784f35SKrzysztof Karas rc = bdevperf_construct_job(bdev, config, thread); 193629784f35SKrzysztof Karas if (rc < 0) { 193729784f35SKrzysztof Karas g_run_rc = rc; 193829784f35SKrzysztof Karas return; 193929784f35SKrzysztof Karas } 194029784f35SKrzysztof Karas } 194129784f35SKrzysztof Karas } 194229784f35SKrzysztof Karas } 194329784f35SKrzysztof Karas 194429784f35SKrzysztof Karas static int 194529784f35SKrzysztof Karas make_cli_job_config(const char *filename, int64_t offset, uint64_t range) 194629784f35SKrzysztof Karas { 194729784f35SKrzysztof Karas struct job_config *config = calloc(1, sizeof(*config)); 194829784f35SKrzysztof Karas 194929784f35SKrzysztof Karas if (config == NULL) { 195029784f35SKrzysztof Karas fprintf(stderr, "Unable to allocate memory for job config\n"); 195129784f35SKrzysztof Karas return -ENOMEM; 195229784f35SKrzysztof Karas } 195329784f35SKrzysztof Karas 195429784f35SKrzysztof Karas config->name = filename; 195529784f35SKrzysztof Karas config->filename = filename; 19568e373044SShuhei Matsumoto config->lcore = _get_next_core(); 195729784f35SKrzysztof Karas spdk_cpuset_zero(&config->cpumask); 19588e373044SShuhei Matsumoto spdk_cpuset_set_cpu(&config->cpumask, config->lcore, true); 195929784f35SKrzysztof Karas config->bs = g_io_size; 196029784f35SKrzysztof Karas config->iodepth = g_queue_depth; 196129784f35SKrzysztof Karas config->rwmixread = g_rw_percentage; 196229784f35SKrzysztof Karas config->offset = offset; 196329784f35SKrzysztof Karas config->length = range; 196429784f35SKrzysztof Karas config->rw = parse_rw(g_workload_type, BDEVPERF_CONFIG_ERROR); 196529784f35SKrzysztof Karas if ((int)config->rw == BDEVPERF_CONFIG_ERROR) { 1966ef9168dcSGangCao free(config); 196729784f35SKrzysztof Karas return -EINVAL; 196829784f35SKrzysztof Karas } 196929784f35SKrzysztof Karas 197029784f35SKrzysztof Karas TAILQ_INSERT_TAIL(&job_config_list, config, link); 197129784f35SKrzysztof Karas return 0; 197229784f35SKrzysztof Karas } 197329784f35SKrzysztof Karas 19749ef9d4bdSShuhei Matsumoto static int 197556479486SShuhei Matsumoto bdevperf_construct_multithread_job_config(void *ctx, struct spdk_bdev *bdev) 19769ef9d4bdSShuhei Matsumoto { 197756479486SShuhei Matsumoto uint32_t *num_cores = ctx; 19789ef9d4bdSShuhei Matsumoto uint32_t i; 19799ef9d4bdSShuhei Matsumoto uint64_t blocks_per_job; 19809ef9d4bdSShuhei Matsumoto int64_t offset; 19819ef9d4bdSShuhei Matsumoto int rc; 19829ef9d4bdSShuhei Matsumoto 198356479486SShuhei Matsumoto blocks_per_job = spdk_bdev_get_num_blocks(bdev) / *num_cores; 19849ef9d4bdSShuhei Matsumoto offset = 0; 19859ef9d4bdSShuhei Matsumoto 19869ef9d4bdSShuhei Matsumoto SPDK_ENV_FOREACH_CORE(i) { 19879ef9d4bdSShuhei Matsumoto rc = make_cli_job_config(spdk_bdev_get_name(bdev), offset, blocks_per_job); 19889ef9d4bdSShuhei Matsumoto if (rc) { 19899ef9d4bdSShuhei Matsumoto return rc; 19909ef9d4bdSShuhei Matsumoto } 19919ef9d4bdSShuhei Matsumoto 19929ef9d4bdSShuhei Matsumoto offset += blocks_per_job; 19939ef9d4bdSShuhei Matsumoto } 19949ef9d4bdSShuhei Matsumoto 19959ef9d4bdSShuhei Matsumoto return 0; 19969ef9d4bdSShuhei Matsumoto } 19979ef9d4bdSShuhei Matsumoto 199829784f35SKrzysztof Karas static void 199929784f35SKrzysztof Karas bdevperf_construct_multithread_job_configs(void) 200029784f35SKrzysztof Karas { 200129784f35SKrzysztof Karas struct spdk_bdev *bdev; 200229784f35SKrzysztof Karas uint32_t i; 200329784f35SKrzysztof Karas uint32_t num_cores; 200429784f35SKrzysztof Karas 200529784f35SKrzysztof Karas num_cores = 0; 200629784f35SKrzysztof Karas SPDK_ENV_FOREACH_CORE(i) { 200729784f35SKrzysztof Karas num_cores++; 200829784f35SKrzysztof Karas } 200929784f35SKrzysztof Karas 201029784f35SKrzysztof Karas if (num_cores == 0) { 201129784f35SKrzysztof Karas g_run_rc = -EINVAL; 201229784f35SKrzysztof Karas return; 201329784f35SKrzysztof Karas } 201429784f35SKrzysztof Karas 201529784f35SKrzysztof Karas if (g_job_bdev_name != NULL) { 201629784f35SKrzysztof Karas bdev = spdk_bdev_get_by_name(g_job_bdev_name); 201729784f35SKrzysztof Karas if (!bdev) { 201829784f35SKrzysztof Karas fprintf(stderr, "Unable to find bdev '%s'\n", g_job_bdev_name); 201929784f35SKrzysztof Karas return; 202029784f35SKrzysztof Karas } 202156479486SShuhei Matsumoto g_run_rc = bdevperf_construct_multithread_job_config(&num_cores, bdev); 202229784f35SKrzysztof Karas } else { 202356479486SShuhei Matsumoto g_run_rc = spdk_for_each_bdev_leaf(&num_cores, bdevperf_construct_multithread_job_config); 202429784f35SKrzysztof Karas } 202556479486SShuhei Matsumoto 202629784f35SKrzysztof Karas } 202756479486SShuhei Matsumoto 202856479486SShuhei Matsumoto static int 202956479486SShuhei Matsumoto bdevperf_construct_job_config(void *ctx, struct spdk_bdev *bdev) 203056479486SShuhei Matsumoto { 203156479486SShuhei Matsumoto /* Construct the job */ 203256479486SShuhei Matsumoto return make_cli_job_config(spdk_bdev_get_name(bdev), 0, 0); 203329784f35SKrzysztof Karas } 203429784f35SKrzysztof Karas 203529784f35SKrzysztof Karas static void 20368e373044SShuhei Matsumoto create_lcore_thread(uint32_t lcore) 20378e373044SShuhei Matsumoto { 20388e373044SShuhei Matsumoto struct lcore_thread *lthread; 20398e373044SShuhei Matsumoto struct spdk_cpuset cpumask = {}; 20408e373044SShuhei Matsumoto char name[32]; 20418e373044SShuhei Matsumoto 20428e373044SShuhei Matsumoto lthread = calloc(1, sizeof(*lthread)); 20438e373044SShuhei Matsumoto assert(lthread != NULL); 20448e373044SShuhei Matsumoto 20458e373044SShuhei Matsumoto lthread->lcore = lcore; 20468e373044SShuhei Matsumoto 20478e373044SShuhei Matsumoto snprintf(name, sizeof(name), "lcore_%u", lcore); 20488e373044SShuhei Matsumoto spdk_cpuset_set_cpu(&cpumask, lcore, true); 20498e373044SShuhei Matsumoto 20508e373044SShuhei Matsumoto lthread->thread = spdk_thread_create(name, &cpumask); 20518e373044SShuhei Matsumoto assert(lthread->thread != NULL); 20528e373044SShuhei Matsumoto 20538e373044SShuhei Matsumoto TAILQ_INSERT_TAIL(&g_lcore_thread_list, lthread, link); 20548e373044SShuhei Matsumoto } 20558e373044SShuhei Matsumoto 20568e373044SShuhei Matsumoto static void 205729784f35SKrzysztof Karas bdevperf_construct_job_configs(void) 205829784f35SKrzysztof Karas { 205929784f35SKrzysztof Karas struct spdk_bdev *bdev; 20608e373044SShuhei Matsumoto uint32_t i; 206129784f35SKrzysztof Karas 206229784f35SKrzysztof Karas /* There are three different modes for allocating jobs. Standard mode 206329784f35SKrzysztof Karas * (the default) creates one spdk_thread per bdev and runs the I/O job there. 206429784f35SKrzysztof Karas * 206529784f35SKrzysztof Karas * The -C flag places bdevperf into "multithread" mode, meaning it creates 206629784f35SKrzysztof Karas * one spdk_thread per bdev PER CORE, and runs a copy of the job on each. 206729784f35SKrzysztof Karas * This runs multiple threads per bdev, effectively. 206829784f35SKrzysztof Karas * 206929784f35SKrzysztof Karas * The -j flag implies "FIO" mode which tries to mimic semantic of FIO jobs. 207029784f35SKrzysztof Karas * In "FIO" mode, threads are spawned per-job instead of per-bdev. 207129784f35SKrzysztof Karas * Each FIO job can be individually parameterized by filename, cpu mask, etc, 207229784f35SKrzysztof Karas * which is different from other modes in that they only support global options. 20738e373044SShuhei Matsumoto * 20748e373044SShuhei Matsumoto * Both for standard mode and "multithread" mode, if the -E flag is specified, 20758e373044SShuhei Matsumoto * it creates one spdk_thread PER CORE. On each core, one spdk_thread is shared by 20768e373044SShuhei Matsumoto * multiple jobs. 207729784f35SKrzysztof Karas */ 207829784f35SKrzysztof Karas 207929784f35SKrzysztof Karas if (g_bdevperf_conf) { 208029784f35SKrzysztof Karas goto end; 208129784f35SKrzysztof Karas } 208229784f35SKrzysztof Karas 20838e373044SShuhei Matsumoto if (g_one_thread_per_lcore) { 20848e373044SShuhei Matsumoto SPDK_ENV_FOREACH_CORE(i) { 20858e373044SShuhei Matsumoto create_lcore_thread(i); 20868e373044SShuhei Matsumoto } 20878e373044SShuhei Matsumoto } 20888e373044SShuhei Matsumoto 20898e373044SShuhei Matsumoto if (g_multithread_mode) { 20908e373044SShuhei Matsumoto bdevperf_construct_multithread_job_configs(); 20918e373044SShuhei Matsumoto } else if (g_job_bdev_name != NULL) { 209229784f35SKrzysztof Karas bdev = spdk_bdev_get_by_name(g_job_bdev_name); 209329784f35SKrzysztof Karas if (bdev) { 209429784f35SKrzysztof Karas /* Construct the job */ 209529784f35SKrzysztof Karas g_run_rc = make_cli_job_config(g_job_bdev_name, 0, 0); 209629784f35SKrzysztof Karas } else { 209729784f35SKrzysztof Karas fprintf(stderr, "Unable to find bdev '%s'\n", g_job_bdev_name); 209829784f35SKrzysztof Karas } 209929784f35SKrzysztof Karas } else { 210056479486SShuhei Matsumoto g_run_rc = spdk_for_each_bdev_leaf(NULL, bdevperf_construct_job_config); 210129784f35SKrzysztof Karas } 210229784f35SKrzysztof Karas 210329784f35SKrzysztof Karas end: 210429784f35SKrzysztof Karas /* Increment initial construct_jobs count so that it will never reach 0 in the middle 210529784f35SKrzysztof Karas * of iteration. 210629784f35SKrzysztof Karas */ 210729784f35SKrzysztof Karas g_construct_job_count = 1; 210829784f35SKrzysztof Karas 210929784f35SKrzysztof Karas if (g_run_rc == 0) { 211029784f35SKrzysztof Karas bdevperf_construct_jobs(); 211129784f35SKrzysztof Karas } 211229784f35SKrzysztof Karas 211329784f35SKrzysztof Karas _bdevperf_construct_job_done(NULL); 211429784f35SKrzysztof Karas } 211529784f35SKrzysztof Karas 211629784f35SKrzysztof Karas static int 211729784f35SKrzysztof Karas parse_uint_option(struct spdk_conf_section *s, const char *name, int def) 211829784f35SKrzysztof Karas { 211929784f35SKrzysztof Karas const char *job_name; 212029784f35SKrzysztof Karas int tmp; 212129784f35SKrzysztof Karas 212229784f35SKrzysztof Karas tmp = spdk_conf_section_get_intval(s, name); 212329784f35SKrzysztof Karas if (tmp == -1) { 212429784f35SKrzysztof Karas /* Field was not found. Check default value 212529784f35SKrzysztof Karas * In [global] section it is ok to have undefined values 212629784f35SKrzysztof Karas * but for other sections it is not ok */ 212729784f35SKrzysztof Karas if (def == BDEVPERF_CONFIG_UNDEFINED) { 212829784f35SKrzysztof Karas job_name = spdk_conf_section_get_name(s); 212929784f35SKrzysztof Karas if (strcmp(job_name, "global") == 0) { 213029784f35SKrzysztof Karas return def; 213129784f35SKrzysztof Karas } 213229784f35SKrzysztof Karas 213329784f35SKrzysztof Karas fprintf(stderr, 213429784f35SKrzysztof Karas "Job '%s' has no '%s' assigned\n", 213529784f35SKrzysztof Karas job_name, name); 213629784f35SKrzysztof Karas return BDEVPERF_CONFIG_ERROR; 213729784f35SKrzysztof Karas } 213829784f35SKrzysztof Karas return def; 213929784f35SKrzysztof Karas } 214029784f35SKrzysztof Karas 214129784f35SKrzysztof Karas /* NOTE: get_intval returns nonnegative on success */ 214229784f35SKrzysztof Karas if (tmp < 0) { 214329784f35SKrzysztof Karas fprintf(stderr, "Job '%s' has bad '%s' value.\n", 214429784f35SKrzysztof Karas spdk_conf_section_get_name(s), name); 214529784f35SKrzysztof Karas return BDEVPERF_CONFIG_ERROR; 214629784f35SKrzysztof Karas } 214729784f35SKrzysztof Karas 214829784f35SKrzysztof Karas return tmp; 214929784f35SKrzysztof Karas } 215029784f35SKrzysztof Karas 215129784f35SKrzysztof Karas /* CLI arguments override parameters for global sections */ 215229784f35SKrzysztof Karas static void 215329784f35SKrzysztof Karas config_set_cli_args(struct job_config *config) 215429784f35SKrzysztof Karas { 215529784f35SKrzysztof Karas if (g_job_bdev_name) { 215629784f35SKrzysztof Karas config->filename = g_job_bdev_name; 215729784f35SKrzysztof Karas } 215829784f35SKrzysztof Karas if (g_io_size > 0) { 215929784f35SKrzysztof Karas config->bs = g_io_size; 216029784f35SKrzysztof Karas } 216129784f35SKrzysztof Karas if (g_queue_depth > 0) { 216229784f35SKrzysztof Karas config->iodepth = g_queue_depth; 216329784f35SKrzysztof Karas } 216429784f35SKrzysztof Karas if (g_rw_percentage > 0) { 216529784f35SKrzysztof Karas config->rwmixread = g_rw_percentage; 216629784f35SKrzysztof Karas } 216729784f35SKrzysztof Karas if (g_workload_type) { 216829784f35SKrzysztof Karas config->rw = parse_rw(g_workload_type, config->rw); 216929784f35SKrzysztof Karas } 217029784f35SKrzysztof Karas } 217129784f35SKrzysztof Karas 217229784f35SKrzysztof Karas static int 217329784f35SKrzysztof Karas read_job_config(void) 217429784f35SKrzysztof Karas { 217529784f35SKrzysztof Karas struct job_config global_default_config; 217629784f35SKrzysztof Karas struct job_config global_config; 217729784f35SKrzysztof Karas struct spdk_conf_section *s; 217829784f35SKrzysztof Karas struct job_config *config; 217929784f35SKrzysztof Karas const char *cpumask; 218029784f35SKrzysztof Karas const char *rw; 218129784f35SKrzysztof Karas bool is_global; 218229784f35SKrzysztof Karas int n = 0; 218329784f35SKrzysztof Karas int val; 218429784f35SKrzysztof Karas 218529784f35SKrzysztof Karas if (g_bdevperf_conf_file == NULL) { 218629784f35SKrzysztof Karas return 0; 218729784f35SKrzysztof Karas } 218829784f35SKrzysztof Karas 218929784f35SKrzysztof Karas g_bdevperf_conf = spdk_conf_allocate(); 219029784f35SKrzysztof Karas if (g_bdevperf_conf == NULL) { 219129784f35SKrzysztof Karas fprintf(stderr, "Could not allocate job config structure\n"); 219229784f35SKrzysztof Karas return 1; 219329784f35SKrzysztof Karas } 219429784f35SKrzysztof Karas 219529784f35SKrzysztof Karas spdk_conf_disable_sections_merge(g_bdevperf_conf); 219629784f35SKrzysztof Karas if (spdk_conf_read(g_bdevperf_conf, g_bdevperf_conf_file)) { 219729784f35SKrzysztof Karas fprintf(stderr, "Invalid job config"); 219829784f35SKrzysztof Karas return 1; 219929784f35SKrzysztof Karas } 220029784f35SKrzysztof Karas 220129784f35SKrzysztof Karas /* Initialize global defaults */ 220229784f35SKrzysztof Karas global_default_config.filename = NULL; 220329784f35SKrzysztof Karas /* Zero mask is the same as g_all_cpuset 220429784f35SKrzysztof Karas * The g_all_cpuset is not initialized yet, 220529784f35SKrzysztof Karas * so use zero mask as the default instead */ 220629784f35SKrzysztof Karas spdk_cpuset_zero(&global_default_config.cpumask); 220729784f35SKrzysztof Karas global_default_config.bs = BDEVPERF_CONFIG_UNDEFINED; 220829784f35SKrzysztof Karas global_default_config.iodepth = BDEVPERF_CONFIG_UNDEFINED; 220929784f35SKrzysztof Karas /* bdevperf has no default for -M option but in FIO the default is 50 */ 221029784f35SKrzysztof Karas global_default_config.rwmixread = 50; 221129784f35SKrzysztof Karas global_default_config.offset = 0; 221229784f35SKrzysztof Karas /* length 0 means 100% */ 221329784f35SKrzysztof Karas global_default_config.length = 0; 221429784f35SKrzysztof Karas global_default_config.rw = BDEVPERF_CONFIG_UNDEFINED; 221529784f35SKrzysztof Karas config_set_cli_args(&global_default_config); 221629784f35SKrzysztof Karas 221729784f35SKrzysztof Karas if ((int)global_default_config.rw == BDEVPERF_CONFIG_ERROR) { 221829784f35SKrzysztof Karas return 1; 221929784f35SKrzysztof Karas } 222029784f35SKrzysztof Karas 222129784f35SKrzysztof Karas /* There is only a single instance of global job_config 222229784f35SKrzysztof Karas * We just reset its value when we encounter new [global] section */ 222329784f35SKrzysztof Karas global_config = global_default_config; 222429784f35SKrzysztof Karas 222529784f35SKrzysztof Karas for (s = spdk_conf_first_section(g_bdevperf_conf); 222629784f35SKrzysztof Karas s != NULL; 222729784f35SKrzysztof Karas s = spdk_conf_next_section(s)) { 222829784f35SKrzysztof Karas config = calloc(1, sizeof(*config)); 222929784f35SKrzysztof Karas if (config == NULL) { 223029784f35SKrzysztof Karas fprintf(stderr, "Unable to allocate memory for job config\n"); 223129784f35SKrzysztof Karas return 1; 223229784f35SKrzysztof Karas } 223329784f35SKrzysztof Karas 223429784f35SKrzysztof Karas config->name = spdk_conf_section_get_name(s); 223529784f35SKrzysztof Karas is_global = strcmp(config->name, "global") == 0; 223629784f35SKrzysztof Karas 223729784f35SKrzysztof Karas if (is_global) { 223829784f35SKrzysztof Karas global_config = global_default_config; 223929784f35SKrzysztof Karas } 224029784f35SKrzysztof Karas 224129784f35SKrzysztof Karas config->filename = spdk_conf_section_get_val(s, "filename"); 224229784f35SKrzysztof Karas if (config->filename == NULL) { 224329784f35SKrzysztof Karas config->filename = global_config.filename; 224429784f35SKrzysztof Karas } 224529784f35SKrzysztof Karas if (!is_global) { 224629784f35SKrzysztof Karas if (config->filename == NULL) { 224729784f35SKrzysztof Karas fprintf(stderr, "Job '%s' expects 'filename' parameter\n", config->name); 224829784f35SKrzysztof Karas goto error; 224929784f35SKrzysztof Karas } else if (strnlen(config->filename, BDEVPERF_CONFIG_MAX_FILENAME) 225029784f35SKrzysztof Karas >= BDEVPERF_CONFIG_MAX_FILENAME) { 225129784f35SKrzysztof Karas fprintf(stderr, 225229784f35SKrzysztof Karas "filename for '%s' job is too long. Max length is %d\n", 225329784f35SKrzysztof Karas config->name, BDEVPERF_CONFIG_MAX_FILENAME); 225429784f35SKrzysztof Karas goto error; 225529784f35SKrzysztof Karas } 225629784f35SKrzysztof Karas } 225729784f35SKrzysztof Karas 225829784f35SKrzysztof Karas cpumask = spdk_conf_section_get_val(s, "cpumask"); 225929784f35SKrzysztof Karas if (cpumask == NULL) { 226029784f35SKrzysztof Karas config->cpumask = global_config.cpumask; 226129784f35SKrzysztof Karas } else if (spdk_cpuset_parse(&config->cpumask, cpumask)) { 226229784f35SKrzysztof Karas fprintf(stderr, "Job '%s' has bad 'cpumask' value\n", config->name); 226329784f35SKrzysztof Karas goto error; 226429784f35SKrzysztof Karas } 226529784f35SKrzysztof Karas 226629784f35SKrzysztof Karas config->bs = parse_uint_option(s, "bs", global_config.bs); 226729784f35SKrzysztof Karas if (config->bs == BDEVPERF_CONFIG_ERROR) { 226829784f35SKrzysztof Karas goto error; 226929784f35SKrzysztof Karas } else if (config->bs == 0) { 227029784f35SKrzysztof Karas fprintf(stderr, "'bs' of job '%s' must be greater than 0\n", config->name); 227129784f35SKrzysztof Karas goto error; 227229784f35SKrzysztof Karas } 227329784f35SKrzysztof Karas 227429784f35SKrzysztof Karas config->iodepth = parse_uint_option(s, "iodepth", global_config.iodepth); 227529784f35SKrzysztof Karas if (config->iodepth == BDEVPERF_CONFIG_ERROR) { 227629784f35SKrzysztof Karas goto error; 227729784f35SKrzysztof Karas } else if (config->iodepth == 0) { 227829784f35SKrzysztof Karas fprintf(stderr, 227929784f35SKrzysztof Karas "'iodepth' of job '%s' must be greater than 0\n", 228029784f35SKrzysztof Karas config->name); 228129784f35SKrzysztof Karas goto error; 228229784f35SKrzysztof Karas } 228329784f35SKrzysztof Karas 228429784f35SKrzysztof Karas config->rwmixread = parse_uint_option(s, "rwmixread", global_config.rwmixread); 228529784f35SKrzysztof Karas if (config->rwmixread == BDEVPERF_CONFIG_ERROR) { 228629784f35SKrzysztof Karas goto error; 228729784f35SKrzysztof Karas } else if (config->rwmixread > 100) { 228829784f35SKrzysztof Karas fprintf(stderr, 228929784f35SKrzysztof Karas "'rwmixread' value of '%s' job is not in 0-100 range\n", 229029784f35SKrzysztof Karas config->name); 229129784f35SKrzysztof Karas goto error; 229229784f35SKrzysztof Karas } 229329784f35SKrzysztof Karas 229429784f35SKrzysztof Karas config->offset = parse_uint_option(s, "offset", global_config.offset); 229529784f35SKrzysztof Karas if (config->offset == BDEVPERF_CONFIG_ERROR) { 229629784f35SKrzysztof Karas goto error; 229729784f35SKrzysztof Karas } 229829784f35SKrzysztof Karas 229929784f35SKrzysztof Karas val = parse_uint_option(s, "length", global_config.length); 230029784f35SKrzysztof Karas if (val == BDEVPERF_CONFIG_ERROR) { 230129784f35SKrzysztof Karas goto error; 230229784f35SKrzysztof Karas } 230329784f35SKrzysztof Karas config->length = val; 230429784f35SKrzysztof Karas 230529784f35SKrzysztof Karas rw = spdk_conf_section_get_val(s, "rw"); 230629784f35SKrzysztof Karas config->rw = parse_rw(rw, global_config.rw); 230729784f35SKrzysztof Karas if ((int)config->rw == BDEVPERF_CONFIG_ERROR) { 230829784f35SKrzysztof Karas fprintf(stderr, "Job '%s' has bad 'rw' value\n", config->name); 230929784f35SKrzysztof Karas goto error; 231029784f35SKrzysztof Karas } else if (!is_global && (int)config->rw == BDEVPERF_CONFIG_UNDEFINED) { 231129784f35SKrzysztof Karas fprintf(stderr, "Job '%s' has no 'rw' assigned\n", config->name); 231229784f35SKrzysztof Karas goto error; 231329784f35SKrzysztof Karas } 231429784f35SKrzysztof Karas 231529784f35SKrzysztof Karas if (is_global) { 231629784f35SKrzysztof Karas config_set_cli_args(config); 231729784f35SKrzysztof Karas global_config = *config; 231829784f35SKrzysztof Karas free(config); 231929784f35SKrzysztof Karas } else { 232029784f35SKrzysztof Karas TAILQ_INSERT_TAIL(&job_config_list, config, link); 232129784f35SKrzysztof Karas n++; 232229784f35SKrzysztof Karas } 232329784f35SKrzysztof Karas } 232429784f35SKrzysztof Karas 232529784f35SKrzysztof Karas printf("Using job config with %d jobs\n", n); 232629784f35SKrzysztof Karas return 0; 232729784f35SKrzysztof Karas error: 232829784f35SKrzysztof Karas free(config); 232929784f35SKrzysztof Karas return 1; 233029784f35SKrzysztof Karas } 233129784f35SKrzysztof Karas 233229784f35SKrzysztof Karas static void 233329784f35SKrzysztof Karas bdevperf_run(void *arg1) 233429784f35SKrzysztof Karas { 233529784f35SKrzysztof Karas uint32_t i; 233629784f35SKrzysztof Karas 233729784f35SKrzysztof Karas g_main_thread = spdk_get_thread(); 233829784f35SKrzysztof Karas 233929784f35SKrzysztof Karas spdk_cpuset_zero(&g_all_cpuset); 234029784f35SKrzysztof Karas SPDK_ENV_FOREACH_CORE(i) { 234129784f35SKrzysztof Karas spdk_cpuset_set_cpu(&g_all_cpuset, i, true); 234229784f35SKrzysztof Karas } 234329784f35SKrzysztof Karas 234429784f35SKrzysztof Karas if (g_wait_for_tests) { 234529784f35SKrzysztof Karas /* Do not perform any tests until RPC is received */ 234629784f35SKrzysztof Karas return; 234729784f35SKrzysztof Karas } 234829784f35SKrzysztof Karas 234929784f35SKrzysztof Karas bdevperf_construct_job_configs(); 235029784f35SKrzysztof Karas } 235129784f35SKrzysztof Karas 235229784f35SKrzysztof Karas static void 2353ed4b89aaSGangCao rpc_perform_tests_reset(void) 2354ed4b89aaSGangCao { 2355ed4b89aaSGangCao /* Reset g_run_rc to 0 for the next test run. */ 2356ed4b89aaSGangCao g_run_rc = 0; 2357ed4b89aaSGangCao 2358ed4b89aaSGangCao /* Reset g_stats to 0 for the next test run. */ 2359ed4b89aaSGangCao memset(&g_stats, 0, sizeof(g_stats)); 2360ed4b89aaSGangCao 2361ed4b89aaSGangCao /* Reset g_show_performance_period_num to 0 for the next test run. */ 2362ed4b89aaSGangCao g_show_performance_period_num = 0; 2363ed4b89aaSGangCao } 2364ed4b89aaSGangCao 2365ed4b89aaSGangCao static void 236629784f35SKrzysztof Karas rpc_perform_tests_cb(void) 236729784f35SKrzysztof Karas { 236829784f35SKrzysztof Karas struct spdk_json_write_ctx *w; 236929784f35SKrzysztof Karas struct spdk_jsonrpc_request *request = g_request; 237029784f35SKrzysztof Karas 237129784f35SKrzysztof Karas g_request = NULL; 237229784f35SKrzysztof Karas 237329784f35SKrzysztof Karas if (g_run_rc == 0) { 237429784f35SKrzysztof Karas w = spdk_jsonrpc_begin_result(request); 237529784f35SKrzysztof Karas spdk_json_write_uint32(w, g_run_rc); 237629784f35SKrzysztof Karas spdk_jsonrpc_end_result(request, w); 237729784f35SKrzysztof Karas } else { 237829784f35SKrzysztof Karas spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, 237929784f35SKrzysztof Karas "bdevperf failed with error %s", spdk_strerror(-g_run_rc)); 238029784f35SKrzysztof Karas } 238129784f35SKrzysztof Karas 2382ed4b89aaSGangCao rpc_perform_tests_reset(); 238329784f35SKrzysztof Karas } 238429784f35SKrzysztof Karas 238529784f35SKrzysztof Karas static void 238629784f35SKrzysztof Karas rpc_perform_tests(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) 238729784f35SKrzysztof Karas { 238829784f35SKrzysztof Karas if (params != NULL) { 238929784f35SKrzysztof Karas spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, 239029784f35SKrzysztof Karas "perform_tests method requires no parameters"); 239129784f35SKrzysztof Karas return; 239229784f35SKrzysztof Karas } 239329784f35SKrzysztof Karas if (g_request != NULL) { 239429784f35SKrzysztof Karas fprintf(stderr, "Another test is already in progress.\n"); 239529784f35SKrzysztof Karas spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, 239629784f35SKrzysztof Karas spdk_strerror(-EINPROGRESS)); 239729784f35SKrzysztof Karas return; 239829784f35SKrzysztof Karas } 239929784f35SKrzysztof Karas g_request = request; 240029784f35SKrzysztof Karas 240129784f35SKrzysztof Karas /* Only construct job configs at the first test run. */ 240229784f35SKrzysztof Karas if (TAILQ_EMPTY(&job_config_list)) { 240329784f35SKrzysztof Karas bdevperf_construct_job_configs(); 240429784f35SKrzysztof Karas } else { 240529784f35SKrzysztof Karas bdevperf_construct_jobs(); 240629784f35SKrzysztof Karas } 240729784f35SKrzysztof Karas } 240829784f35SKrzysztof Karas SPDK_RPC_REGISTER("perform_tests", rpc_perform_tests, SPDK_RPC_RUNTIME) 240929784f35SKrzysztof Karas 241029784f35SKrzysztof Karas static void 241129784f35SKrzysztof Karas _bdevperf_job_drain(void *ctx) 241229784f35SKrzysztof Karas { 241329784f35SKrzysztof Karas bdevperf_job_drain(ctx); 241429784f35SKrzysztof Karas } 241529784f35SKrzysztof Karas 241629784f35SKrzysztof Karas static void 241729784f35SKrzysztof Karas spdk_bdevperf_shutdown_cb(void) 241829784f35SKrzysztof Karas { 241929784f35SKrzysztof Karas g_shutdown = true; 242029784f35SKrzysztof Karas struct bdevperf_job *job, *tmp; 242129784f35SKrzysztof Karas 242229784f35SKrzysztof Karas if (g_bdevperf.running_jobs == 0) { 242329784f35SKrzysztof Karas bdevperf_test_done(NULL); 242429784f35SKrzysztof Karas return; 242529784f35SKrzysztof Karas } 242629784f35SKrzysztof Karas 242729784f35SKrzysztof Karas /* Iterate jobs to stop all I/O */ 242829784f35SKrzysztof Karas TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, tmp) { 242929784f35SKrzysztof Karas spdk_thread_send_msg(job->thread, _bdevperf_job_drain, job); 243029784f35SKrzysztof Karas } 243129784f35SKrzysztof Karas } 243229784f35SKrzysztof Karas 243329784f35SKrzysztof Karas static int 243429784f35SKrzysztof Karas bdevperf_parse_arg(int ch, char *arg) 243529784f35SKrzysztof Karas { 243629784f35SKrzysztof Karas long long tmp; 243729784f35SKrzysztof Karas 243829784f35SKrzysztof Karas if (ch == 'w') { 243929784f35SKrzysztof Karas g_workload_type = optarg; 244029784f35SKrzysztof Karas } else if (ch == 'T') { 244129784f35SKrzysztof Karas g_job_bdev_name = optarg; 244229784f35SKrzysztof Karas } else if (ch == 'z') { 244329784f35SKrzysztof Karas g_wait_for_tests = true; 244429784f35SKrzysztof Karas } else if (ch == 'Z') { 244529784f35SKrzysztof Karas g_zcopy = true; 244629784f35SKrzysztof Karas } else if (ch == 'X') { 244729784f35SKrzysztof Karas g_abort = true; 244829784f35SKrzysztof Karas } else if (ch == 'C') { 244929784f35SKrzysztof Karas g_multithread_mode = true; 245029784f35SKrzysztof Karas } else if (ch == 'f') { 245129784f35SKrzysztof Karas g_continue_on_failure = true; 245229784f35SKrzysztof Karas } else if (ch == 'j') { 245329784f35SKrzysztof Karas g_bdevperf_conf_file = optarg; 245429784f35SKrzysztof Karas } else if (ch == 'F') { 245529784f35SKrzysztof Karas char *endptr; 245629784f35SKrzysztof Karas 245729784f35SKrzysztof Karas errno = 0; 245829784f35SKrzysztof Karas g_zipf_theta = strtod(optarg, &endptr); 245929784f35SKrzysztof Karas if (errno || optarg == endptr || g_zipf_theta < 0) { 246029784f35SKrzysztof Karas fprintf(stderr, "Illegal zipf theta value %s\n", optarg); 246129784f35SKrzysztof Karas return -EINVAL; 246229784f35SKrzysztof Karas } 2463723dd06eSRichael Zhuang } else if (ch == 'l') { 2464723dd06eSRichael Zhuang g_latency_display_level++; 2465d254a3b9SSlawomir Ptak } else if (ch == 'D') { 2466d254a3b9SSlawomir Ptak g_random_map = true; 24678e373044SShuhei Matsumoto } else if (ch == 'E') { 24688e373044SShuhei Matsumoto g_one_thread_per_lcore = true; 246929784f35SKrzysztof Karas } else { 247029784f35SKrzysztof Karas tmp = spdk_strtoll(optarg, 10); 247129784f35SKrzysztof Karas if (tmp < 0) { 247229784f35SKrzysztof Karas fprintf(stderr, "Parse failed for the option %c.\n", ch); 247329784f35SKrzysztof Karas return tmp; 247429784f35SKrzysztof Karas } else if (tmp >= INT_MAX) { 247529784f35SKrzysztof Karas fprintf(stderr, "Parsed option was too large %c.\n", ch); 247629784f35SKrzysztof Karas return -ERANGE; 247729784f35SKrzysztof Karas } 247829784f35SKrzysztof Karas 247929784f35SKrzysztof Karas switch (ch) { 248029784f35SKrzysztof Karas case 'q': 248129784f35SKrzysztof Karas g_queue_depth = tmp; 248229784f35SKrzysztof Karas break; 248329784f35SKrzysztof Karas case 'o': 248429784f35SKrzysztof Karas g_io_size = tmp; 248529784f35SKrzysztof Karas break; 248629784f35SKrzysztof Karas case 't': 248729784f35SKrzysztof Karas g_time_in_sec = tmp; 248829784f35SKrzysztof Karas break; 248929784f35SKrzysztof Karas case 'k': 249029784f35SKrzysztof Karas g_timeout_in_sec = tmp; 249129784f35SKrzysztof Karas break; 249229784f35SKrzysztof Karas case 'M': 249329784f35SKrzysztof Karas g_rw_percentage = tmp; 249429784f35SKrzysztof Karas g_mix_specified = true; 249529784f35SKrzysztof Karas break; 249629784f35SKrzysztof Karas case 'P': 249729784f35SKrzysztof Karas g_show_performance_ema_period = tmp; 249829784f35SKrzysztof Karas break; 249929784f35SKrzysztof Karas case 'S': 250029784f35SKrzysztof Karas g_show_performance_real_time = 1; 2501045c781dSRichael Zhuang g_show_performance_period_in_usec = tmp * SPDK_SEC_TO_USEC; 250229784f35SKrzysztof Karas break; 250329784f35SKrzysztof Karas default: 250429784f35SKrzysztof Karas return -EINVAL; 250529784f35SKrzysztof Karas } 250629784f35SKrzysztof Karas } 250729784f35SKrzysztof Karas return 0; 250829784f35SKrzysztof Karas } 250929784f35SKrzysztof Karas 251029784f35SKrzysztof Karas static void 251129784f35SKrzysztof Karas bdevperf_usage(void) 251229784f35SKrzysztof Karas { 251329784f35SKrzysztof Karas printf(" -q <depth> io depth\n"); 251429784f35SKrzysztof Karas printf(" -o <size> io size in bytes\n"); 251529784f35SKrzysztof Karas printf(" -w <type> io pattern type, must be one of (read, write, randread, randwrite, rw, randrw, verify, reset, unmap, flush)\n"); 251629784f35SKrzysztof Karas printf(" -t <time> time in seconds\n"); 251729784f35SKrzysztof Karas printf(" -k <timeout> timeout in seconds to detect starved I/O (default is 0 and disabled)\n"); 251829784f35SKrzysztof Karas printf(" -M <percent> rwmixread (100 for reads, 0 for writes)\n"); 251929784f35SKrzysztof Karas printf(" -P <num> number of moving average period\n"); 252029784f35SKrzysztof Karas printf("\t\t(If set to n, show weighted mean of the previous n IO/s in real time)\n"); 252129784f35SKrzysztof Karas printf("\t\t(Formula: M = 2 / (n + 1), EMA[i+1] = IO/s * M + (1 - M) * EMA[i])\n"); 252229784f35SKrzysztof Karas printf("\t\t(only valid with -S)\n"); 252329784f35SKrzysztof Karas printf(" -S <period> show performance result in real time every <period> seconds\n"); 252429784f35SKrzysztof Karas printf(" -T <bdev> bdev to run against. Default: all available bdevs.\n"); 252529784f35SKrzysztof Karas printf(" -f continue processing I/O even after failures\n"); 252629784f35SKrzysztof Karas printf(" -F <zipf theta> use zipf distribution for random I/O\n"); 252729784f35SKrzysztof Karas printf(" -Z enable using zcopy bdev API for read or write I/O\n"); 252829784f35SKrzysztof Karas printf(" -z start bdevperf, but wait for RPC to start tests\n"); 252929784f35SKrzysztof Karas printf(" -X abort timed out I/O\n"); 253029784f35SKrzysztof Karas printf(" -C enable every core to send I/Os to each bdev\n"); 253129784f35SKrzysztof Karas printf(" -j <filename> use job config file\n"); 2532723dd06eSRichael Zhuang printf(" -l display latency histogram, default: disable. -l display summary, -ll display details\n"); 2533d254a3b9SSlawomir Ptak printf(" -D use a random map for picking offsets not previously read or written (for all jobs)\n"); 25348e373044SShuhei Matsumoto printf(" -E share per lcore thread among jobs. Available only if -j is not used.\n"); 253529784f35SKrzysztof Karas } 253629784f35SKrzysztof Karas 2537*a1f4f11bSShuhei Matsumoto static void 2538*a1f4f11bSShuhei Matsumoto bdevperf_fini(void) 2539*a1f4f11bSShuhei Matsumoto { 2540*a1f4f11bSShuhei Matsumoto free_job_config(); 2541*a1f4f11bSShuhei Matsumoto } 2542*a1f4f11bSShuhei Matsumoto 254329784f35SKrzysztof Karas static int 254429784f35SKrzysztof Karas verify_test_params(struct spdk_app_opts *opts) 254529784f35SKrzysztof Karas { 254629784f35SKrzysztof Karas /* When RPC is used for starting tests and 254729784f35SKrzysztof Karas * no rpc_addr was configured for the app, 254829784f35SKrzysztof Karas * use the default address. */ 254929784f35SKrzysztof Karas if (g_wait_for_tests && opts->rpc_addr == NULL) { 255029784f35SKrzysztof Karas opts->rpc_addr = SPDK_DEFAULT_RPC_ADDR; 255129784f35SKrzysztof Karas } 255229784f35SKrzysztof Karas 255329784f35SKrzysztof Karas if (!g_bdevperf_conf_file && g_queue_depth <= 0) { 255429784f35SKrzysztof Karas goto out; 255529784f35SKrzysztof Karas } 255629784f35SKrzysztof Karas if (!g_bdevperf_conf_file && g_io_size <= 0) { 255729784f35SKrzysztof Karas goto out; 255829784f35SKrzysztof Karas } 255929784f35SKrzysztof Karas if (!g_bdevperf_conf_file && !g_workload_type) { 256029784f35SKrzysztof Karas goto out; 256129784f35SKrzysztof Karas } 25628e373044SShuhei Matsumoto if (g_bdevperf_conf_file && g_one_thread_per_lcore) { 25638e373044SShuhei Matsumoto printf("If bdevperf's config file is used, per lcore thread cannot be used\n"); 25648e373044SShuhei Matsumoto goto out; 25658e373044SShuhei Matsumoto } 256629784f35SKrzysztof Karas if (g_time_in_sec <= 0) { 256729784f35SKrzysztof Karas goto out; 256829784f35SKrzysztof Karas } 2569045c781dSRichael Zhuang g_time_in_usec = g_time_in_sec * SPDK_SEC_TO_USEC; 257029784f35SKrzysztof Karas 257129784f35SKrzysztof Karas if (g_timeout_in_sec < 0) { 257229784f35SKrzysztof Karas goto out; 257329784f35SKrzysztof Karas } 257429784f35SKrzysztof Karas 257529784f35SKrzysztof Karas if (g_abort && !g_timeout_in_sec) { 257629784f35SKrzysztof Karas printf("Timeout must be set for abort option, Ignoring g_abort\n"); 257729784f35SKrzysztof Karas } 257829784f35SKrzysztof Karas 257929784f35SKrzysztof Karas if (g_show_performance_ema_period > 0 && 258029784f35SKrzysztof Karas g_show_performance_real_time == 0) { 258129784f35SKrzysztof Karas fprintf(stderr, "-P option must be specified with -S option\n"); 258229784f35SKrzysztof Karas return 1; 258329784f35SKrzysztof Karas } 258429784f35SKrzysztof Karas 258529784f35SKrzysztof Karas if (g_io_size > SPDK_BDEV_LARGE_BUF_MAX_SIZE) { 258629784f35SKrzysztof Karas printf("I/O size of %d is greater than zero copy threshold (%d).\n", 258729784f35SKrzysztof Karas g_io_size, SPDK_BDEV_LARGE_BUF_MAX_SIZE); 258829784f35SKrzysztof Karas printf("Zero copy mechanism will not be used.\n"); 258929784f35SKrzysztof Karas g_zcopy = false; 259029784f35SKrzysztof Karas } 259129784f35SKrzysztof Karas 259229784f35SKrzysztof Karas if (g_bdevperf_conf_file) { 259329784f35SKrzysztof Karas /* workload_type verification happens during config file parsing */ 259429784f35SKrzysztof Karas return 0; 259529784f35SKrzysztof Karas } 259629784f35SKrzysztof Karas 259729784f35SKrzysztof Karas if (!strcmp(g_workload_type, "verify") || 259829784f35SKrzysztof Karas !strcmp(g_workload_type, "reset")) { 259929784f35SKrzysztof Karas g_rw_percentage = 50; 260029784f35SKrzysztof Karas if (g_io_size > SPDK_BDEV_LARGE_BUF_MAX_SIZE) { 260129784f35SKrzysztof Karas fprintf(stderr, "Unable to exceed max I/O size of %d for verify. (%d provided).\n", 260229784f35SKrzysztof Karas SPDK_BDEV_LARGE_BUF_MAX_SIZE, g_io_size); 260329784f35SKrzysztof Karas return 1; 260429784f35SKrzysztof Karas } 260529784f35SKrzysztof Karas g_verify = true; 260629784f35SKrzysztof Karas if (!strcmp(g_workload_type, "reset")) { 260729784f35SKrzysztof Karas g_reset = true; 260829784f35SKrzysztof Karas } 260929784f35SKrzysztof Karas } 261029784f35SKrzysztof Karas 261129784f35SKrzysztof Karas if (!strcmp(g_workload_type, "read") || 261229784f35SKrzysztof Karas !strcmp(g_workload_type, "randread") || 261329784f35SKrzysztof Karas !strcmp(g_workload_type, "write") || 261429784f35SKrzysztof Karas !strcmp(g_workload_type, "randwrite") || 261529784f35SKrzysztof Karas !strcmp(g_workload_type, "verify") || 261629784f35SKrzysztof Karas !strcmp(g_workload_type, "reset") || 261729784f35SKrzysztof Karas !strcmp(g_workload_type, "unmap") || 261829784f35SKrzysztof Karas !strcmp(g_workload_type, "write_zeroes") || 261929784f35SKrzysztof Karas !strcmp(g_workload_type, "flush")) { 262029784f35SKrzysztof Karas if (g_mix_specified) { 262129784f35SKrzysztof Karas fprintf(stderr, "Ignoring -M option... Please use -M option" 262229784f35SKrzysztof Karas " only when using rw or randrw.\n"); 262329784f35SKrzysztof Karas } 262429784f35SKrzysztof Karas } 262529784f35SKrzysztof Karas 262629784f35SKrzysztof Karas if (!strcmp(g_workload_type, "rw") || 262729784f35SKrzysztof Karas !strcmp(g_workload_type, "randrw")) { 262829784f35SKrzysztof Karas if (g_rw_percentage < 0 || g_rw_percentage > 100) { 262929784f35SKrzysztof Karas fprintf(stderr, 263029784f35SKrzysztof Karas "-M must be specified to value from 0 to 100 " 263129784f35SKrzysztof Karas "for rw or randrw.\n"); 263229784f35SKrzysztof Karas return 1; 263329784f35SKrzysztof Karas } 263429784f35SKrzysztof Karas } 263529784f35SKrzysztof Karas 2636d254a3b9SSlawomir Ptak if (strcmp(g_workload_type, "randread") && 2637d254a3b9SSlawomir Ptak strcmp(g_workload_type, "randwrite") && 2638d254a3b9SSlawomir Ptak strcmp(g_workload_type, "randrw")) { 2639d254a3b9SSlawomir Ptak if (g_random_map) { 2640d254a3b9SSlawomir Ptak fprintf(stderr, "Ignoring -D option... Please use -D option" 2641d254a3b9SSlawomir Ptak " only when using randread, randwrite or randrw.\n"); 2642d254a3b9SSlawomir Ptak return 1; 2643d254a3b9SSlawomir Ptak } 2644d254a3b9SSlawomir Ptak } 2645d254a3b9SSlawomir Ptak 264629784f35SKrzysztof Karas return 0; 264729784f35SKrzysztof Karas out: 264829784f35SKrzysztof Karas spdk_app_usage(); 264929784f35SKrzysztof Karas bdevperf_usage(); 265029784f35SKrzysztof Karas return 1; 265129784f35SKrzysztof Karas } 265229784f35SKrzysztof Karas 265329784f35SKrzysztof Karas int 265429784f35SKrzysztof Karas main(int argc, char **argv) 265529784f35SKrzysztof Karas { 265629784f35SKrzysztof Karas struct spdk_app_opts opts = {}; 265729784f35SKrzysztof Karas int rc; 265829784f35SKrzysztof Karas 265929784f35SKrzysztof Karas /* Use the runtime PID to set the random seed */ 266029784f35SKrzysztof Karas srand(getpid()); 266129784f35SKrzysztof Karas 266229784f35SKrzysztof Karas spdk_app_opts_init(&opts, sizeof(opts)); 266329784f35SKrzysztof Karas opts.name = "bdevperf"; 266429784f35SKrzysztof Karas opts.rpc_addr = NULL; 266529784f35SKrzysztof Karas opts.shutdown_cb = spdk_bdevperf_shutdown_cb; 266629784f35SKrzysztof Karas 26678e373044SShuhei Matsumoto if ((rc = spdk_app_parse_args(argc, argv, &opts, "Zzfq:o:t:w:k:CEF:M:P:S:T:Xlj:D", NULL, 266829784f35SKrzysztof Karas bdevperf_parse_arg, bdevperf_usage)) != 266929784f35SKrzysztof Karas SPDK_APP_PARSE_ARGS_SUCCESS) { 267029784f35SKrzysztof Karas return rc; 267129784f35SKrzysztof Karas } 267229784f35SKrzysztof Karas 267329784f35SKrzysztof Karas if (read_job_config()) { 2674*a1f4f11bSShuhei Matsumoto bdevperf_fini(); 267529784f35SKrzysztof Karas return 1; 267629784f35SKrzysztof Karas } 267729784f35SKrzysztof Karas 267829784f35SKrzysztof Karas if (verify_test_params(&opts) != 0) { 2679*a1f4f11bSShuhei Matsumoto bdevperf_fini(); 268029784f35SKrzysztof Karas exit(1); 268129784f35SKrzysztof Karas } 268229784f35SKrzysztof Karas 268329784f35SKrzysztof Karas rc = spdk_app_start(&opts, bdevperf_run, NULL); 268429784f35SKrzysztof Karas 268529784f35SKrzysztof Karas spdk_app_fini(); 2686*a1f4f11bSShuhei Matsumoto bdevperf_fini(); 268729784f35SKrzysztof Karas return rc; 268829784f35SKrzysztof Karas } 2689