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 2732e5ce31SAmir Haroush #define PATTERN_TYPES_STR "(read, write, randread, randwrite, rw, randrw, verify, reset, unmap, flush, write_zeroes)" 283a141b72SMarcin Spiewak #define BDEVPERF_MAX_COREMASK_STRING 64 2929784f35SKrzysztof Karas 3029784f35SKrzysztof Karas struct bdevperf_task { 3129784f35SKrzysztof Karas struct iovec iov; 3229784f35SKrzysztof Karas struct bdevperf_job *job; 3329784f35SKrzysztof Karas struct spdk_bdev_io *bdev_io; 3429784f35SKrzysztof Karas void *buf; 35aa824ae6SOlivier Mayer void *verify_buf; 3629784f35SKrzysztof Karas void *md_buf; 37dc3ea9d2SSlawomir Ptak void *verify_md_buf; 3829784f35SKrzysztof Karas uint64_t offset_blocks; 3929784f35SKrzysztof Karas struct bdevperf_task *task_to_abort; 4029784f35SKrzysztof Karas enum spdk_bdev_io_type io_type; 4129784f35SKrzysztof Karas TAILQ_ENTRY(bdevperf_task) link; 4229784f35SKrzysztof Karas struct spdk_bdev_io_wait_entry bdev_io_wait; 4329784f35SKrzysztof Karas }; 4429784f35SKrzysztof Karas 45d1215940SEugene Kobyak static char *g_workload_type = NULL; 4629784f35SKrzysztof Karas static int g_io_size = 0; 4729784f35SKrzysztof Karas /* initialize to invalid value so we can detect if user overrides it. */ 4829784f35SKrzysztof Karas static int g_rw_percentage = -1; 4929784f35SKrzysztof Karas static bool g_verify = false; 5029784f35SKrzysztof Karas static bool g_reset = false; 5129784f35SKrzysztof Karas static bool g_continue_on_failure = false; 5229784f35SKrzysztof Karas static bool g_abort = false; 5329784f35SKrzysztof Karas static bool g_error_to_exit = false; 5429784f35SKrzysztof Karas static int g_queue_depth = 0; 5529784f35SKrzysztof Karas static uint64_t g_time_in_usec; 5627134348SAnisa Su static bool g_summarize_performance = true; 57045c781dSRichael Zhuang static uint64_t g_show_performance_period_in_usec = SPDK_SEC_TO_USEC; 5829784f35SKrzysztof Karas static uint64_t g_show_performance_period_num = 0; 5929784f35SKrzysztof Karas static uint64_t g_show_performance_ema_period = 0; 6029784f35SKrzysztof Karas static int g_run_rc = 0; 6129784f35SKrzysztof Karas static bool g_shutdown = false; 6229784f35SKrzysztof Karas static uint64_t g_start_tsc; 6329784f35SKrzysztof Karas static uint64_t g_shutdown_tsc; 6429784f35SKrzysztof Karas static bool g_zcopy = false; 6529784f35SKrzysztof Karas static struct spdk_thread *g_main_thread; 6629784f35SKrzysztof Karas static int g_time_in_sec = 0; 6729784f35SKrzysztof Karas static bool g_mix_specified = false; 6829784f35SKrzysztof Karas static const char *g_job_bdev_name; 6929784f35SKrzysztof Karas static bool g_wait_for_tests = false; 7029784f35SKrzysztof Karas static struct spdk_jsonrpc_request *g_request = NULL; 7129784f35SKrzysztof Karas static bool g_multithread_mode = false; 7229784f35SKrzysztof Karas static int g_timeout_in_sec; 7329784f35SKrzysztof Karas static struct spdk_conf *g_bdevperf_conf = NULL; 7429784f35SKrzysztof Karas static const char *g_bdevperf_conf_file = NULL; 7529784f35SKrzysztof Karas static double g_zipf_theta; 76d254a3b9SSlawomir Ptak static bool g_random_map = false; 773353236fSArtur Paszkiewicz static bool g_unique_writes = false; 78*971ec012SShuhei Matsumoto static bool g_hide_metadata = false; 7929784f35SKrzysztof Karas 8029784f35SKrzysztof Karas static struct spdk_cpuset g_all_cpuset; 8129784f35SKrzysztof Karas static struct spdk_poller *g_perf_timer = NULL; 8229784f35SKrzysztof Karas 8329784f35SKrzysztof Karas static void bdevperf_submit_single(struct bdevperf_job *job, struct bdevperf_task *task); 8429784f35SKrzysztof Karas static void rpc_perform_tests_cb(void); 85bbf8dfb7SEugene Kobyak static int bdevperf_parse_arg(int ch, char *arg); 86bbf8dfb7SEugene Kobyak static int verify_test_params(void); 87bbf8dfb7SEugene Kobyak static void bdevperf_usage(void); 8829784f35SKrzysztof Karas 89723dd06eSRichael Zhuang static uint32_t g_bdev_count = 0; 90723dd06eSRichael Zhuang static uint32_t g_latency_display_level; 91723dd06eSRichael Zhuang 928e373044SShuhei Matsumoto static bool g_one_thread_per_lcore = false; 938e373044SShuhei Matsumoto 94723dd06eSRichael Zhuang static const double g_latency_cutoffs[] = { 95723dd06eSRichael Zhuang 0.01, 96723dd06eSRichael Zhuang 0.10, 97723dd06eSRichael Zhuang 0.25, 98723dd06eSRichael Zhuang 0.50, 99723dd06eSRichael Zhuang 0.75, 100723dd06eSRichael Zhuang 0.90, 101723dd06eSRichael Zhuang 0.95, 102723dd06eSRichael Zhuang 0.98, 103723dd06eSRichael Zhuang 0.99, 104723dd06eSRichael Zhuang 0.995, 105723dd06eSRichael Zhuang 0.999, 106723dd06eSRichael Zhuang 0.9999, 107723dd06eSRichael Zhuang 0.99999, 108723dd06eSRichael Zhuang 0.999999, 109723dd06eSRichael Zhuang 0.9999999, 110723dd06eSRichael Zhuang -1, 111723dd06eSRichael Zhuang }; 112723dd06eSRichael Zhuang 113ba87571dSShuhei Matsumoto static const char *g_rpc_log_file_name = NULL; 114ba87571dSShuhei Matsumoto static FILE *g_rpc_log_file = NULL; 115ba87571dSShuhei Matsumoto 116723dd06eSRichael Zhuang struct latency_info { 117723dd06eSRichael Zhuang uint64_t min; 118723dd06eSRichael Zhuang uint64_t max; 119723dd06eSRichael Zhuang uint64_t total; 120723dd06eSRichael Zhuang }; 121723dd06eSRichael Zhuang 1224348ccc0SEugene Kobyak 1234348ccc0SEugene Kobyak enum job_config_rw { 1244348ccc0SEugene Kobyak JOB_CONFIG_RW_READ = 0, 1254348ccc0SEugene Kobyak JOB_CONFIG_RW_WRITE, 1264348ccc0SEugene Kobyak JOB_CONFIG_RW_RANDREAD, 1274348ccc0SEugene Kobyak JOB_CONFIG_RW_RANDWRITE, 1284348ccc0SEugene Kobyak JOB_CONFIG_RW_RW, 1294348ccc0SEugene Kobyak JOB_CONFIG_RW_RANDRW, 1304348ccc0SEugene Kobyak JOB_CONFIG_RW_VERIFY, 1314348ccc0SEugene Kobyak JOB_CONFIG_RW_RESET, 1324348ccc0SEugene Kobyak JOB_CONFIG_RW_UNMAP, 1334348ccc0SEugene Kobyak JOB_CONFIG_RW_FLUSH, 1344348ccc0SEugene Kobyak JOB_CONFIG_RW_WRITE_ZEROES, 1354348ccc0SEugene Kobyak }; 1364348ccc0SEugene Kobyak 13729784f35SKrzysztof Karas struct bdevperf_job { 13829784f35SKrzysztof Karas char *name; 13929784f35SKrzysztof Karas struct spdk_bdev *bdev; 14029784f35SKrzysztof Karas struct spdk_bdev_desc *bdev_desc; 14129784f35SKrzysztof Karas struct spdk_io_channel *ch; 14229784f35SKrzysztof Karas TAILQ_ENTRY(bdevperf_job) link; 14329784f35SKrzysztof Karas struct spdk_thread *thread; 14429784f35SKrzysztof Karas 1454348ccc0SEugene Kobyak enum job_config_rw workload_type; 14629784f35SKrzysztof Karas int io_size; 14729784f35SKrzysztof Karas int rw_percentage; 14829784f35SKrzysztof Karas bool is_random; 14929784f35SKrzysztof Karas bool verify; 15029784f35SKrzysztof Karas bool reset; 15129784f35SKrzysztof Karas bool continue_on_failure; 15229784f35SKrzysztof Karas bool unmap; 15329784f35SKrzysztof Karas bool write_zeroes; 15429784f35SKrzysztof Karas bool flush; 15529784f35SKrzysztof Karas bool abort; 15629784f35SKrzysztof Karas int queue_depth; 15729784f35SKrzysztof Karas unsigned int seed; 15829784f35SKrzysztof Karas 15929784f35SKrzysztof Karas uint64_t io_completed; 16029784f35SKrzysztof Karas uint64_t io_failed; 16129784f35SKrzysztof Karas uint64_t io_timeout; 16229784f35SKrzysztof Karas uint64_t prev_io_completed; 16329784f35SKrzysztof Karas double ema_io_per_second; 16429784f35SKrzysztof Karas int current_queue_depth; 16529784f35SKrzysztof Karas uint64_t size_in_ios; 16629784f35SKrzysztof Karas uint64_t ios_base; 16729784f35SKrzysztof Karas uint64_t offset_in_ios; 16829784f35SKrzysztof Karas uint64_t io_size_blocks; 16929784f35SKrzysztof Karas uint64_t buf_size; 17029784f35SKrzysztof Karas uint32_t dif_check_flags; 17129784f35SKrzysztof Karas bool is_draining; 172075fb5b8SShuhei Matsumoto bool md_check; 17329784f35SKrzysztof Karas struct spdk_poller *run_timer; 17429784f35SKrzysztof Karas struct spdk_poller *reset_timer; 17529784f35SKrzysztof Karas struct spdk_bit_array *outstanding; 17629784f35SKrzysztof Karas struct spdk_zipf *zipf; 17729784f35SKrzysztof Karas TAILQ_HEAD(, bdevperf_task) task_list; 17829784f35SKrzysztof Karas uint64_t run_time_in_usec; 179723dd06eSRichael Zhuang 180723dd06eSRichael Zhuang /* keep channel's histogram data before being destroyed */ 181723dd06eSRichael Zhuang struct spdk_histogram_data *histogram; 182d254a3b9SSlawomir Ptak struct spdk_bit_array *random_map; 1833353236fSArtur Paszkiewicz 1843353236fSArtur Paszkiewicz /* counter used for generating unique write data (-U option) */ 1853353236fSArtur Paszkiewicz uint32_t write_io_count; 18629784f35SKrzysztof Karas }; 18729784f35SKrzysztof Karas 18829784f35SKrzysztof Karas struct spdk_bdevperf { 18929784f35SKrzysztof Karas TAILQ_HEAD(, bdevperf_job) jobs; 19029784f35SKrzysztof Karas uint32_t running_jobs; 19129784f35SKrzysztof Karas }; 19229784f35SKrzysztof Karas 19329784f35SKrzysztof Karas static struct spdk_bdevperf g_bdevperf = { 19429784f35SKrzysztof Karas .jobs = TAILQ_HEAD_INITIALIZER(g_bdevperf.jobs), 19529784f35SKrzysztof Karas .running_jobs = 0, 19629784f35SKrzysztof Karas }; 19729784f35SKrzysztof Karas 19829784f35SKrzysztof Karas /* Storing values from a section of job config file */ 19929784f35SKrzysztof Karas struct job_config { 20029784f35SKrzysztof Karas const char *name; 20129784f35SKrzysztof Karas const char *filename; 20229784f35SKrzysztof Karas struct spdk_cpuset cpumask; 20329784f35SKrzysztof Karas int bs; 20429784f35SKrzysztof Karas int iodepth; 20529784f35SKrzysztof Karas int rwmixread; 2068e373044SShuhei Matsumoto uint32_t lcore; 20729784f35SKrzysztof Karas int64_t offset; 20829784f35SKrzysztof Karas uint64_t length; 20929784f35SKrzysztof Karas enum job_config_rw rw; 21029784f35SKrzysztof Karas TAILQ_ENTRY(job_config) link; 21129784f35SKrzysztof Karas }; 21229784f35SKrzysztof Karas 21329784f35SKrzysztof Karas TAILQ_HEAD(, job_config) job_config_list 21429784f35SKrzysztof Karas = TAILQ_HEAD_INITIALIZER(job_config_list); 21529784f35SKrzysztof Karas 21629784f35SKrzysztof Karas static bool g_performance_dump_active = false; 21729784f35SKrzysztof Karas 218c273168dSMarcin Spiewak struct bdevperf_stats { 21929784f35SKrzysztof Karas uint64_t io_time_in_usec; 22029784f35SKrzysztof Karas double total_io_per_second; 22129784f35SKrzysztof Karas double total_mb_per_second; 22229784f35SKrzysztof Karas double total_failed_per_second; 22329784f35SKrzysztof Karas double total_timeout_per_second; 224723dd06eSRichael Zhuang double min_latency; 225723dd06eSRichael Zhuang double max_latency; 22672177d39SMarcin Spiewak double average_latency; 227723dd06eSRichael Zhuang uint64_t total_io_completed; 228723dd06eSRichael Zhuang uint64_t total_tsc; 22929784f35SKrzysztof Karas }; 23029784f35SKrzysztof Karas 231c273168dSMarcin Spiewak struct bdevperf_aggregate_stats { 232c273168dSMarcin Spiewak struct bdevperf_job *current_job; 233c273168dSMarcin Spiewak struct bdevperf_stats total; 234c273168dSMarcin Spiewak }; 235c273168dSMarcin Spiewak 236c273168dSMarcin Spiewak static struct bdevperf_aggregate_stats g_stats = {.total.min_latency = (double)UINT64_MAX}; 23729784f35SKrzysztof Karas 2388e373044SShuhei Matsumoto struct lcore_thread { 2398e373044SShuhei Matsumoto struct spdk_thread *thread; 2408e373044SShuhei Matsumoto uint32_t lcore; 2418e373044SShuhei Matsumoto TAILQ_ENTRY(lcore_thread) link; 2428e373044SShuhei Matsumoto }; 2438e373044SShuhei Matsumoto 2448e373044SShuhei Matsumoto TAILQ_HEAD(, lcore_thread) g_lcore_thread_list 2458e373044SShuhei Matsumoto = TAILQ_HEAD_INITIALIZER(g_lcore_thread_list); 2468e373044SShuhei Matsumoto 2474348ccc0SEugene Kobyak 2484348ccc0SEugene Kobyak static char * 2494348ccc0SEugene Kobyak parse_workload_type(enum job_config_rw ret) 2504348ccc0SEugene Kobyak { 2514348ccc0SEugene Kobyak switch (ret) { 2524348ccc0SEugene Kobyak case JOB_CONFIG_RW_READ: 2534348ccc0SEugene Kobyak return "read"; 2544348ccc0SEugene Kobyak case JOB_CONFIG_RW_RANDREAD: 2554348ccc0SEugene Kobyak return "randread"; 2564348ccc0SEugene Kobyak case JOB_CONFIG_RW_WRITE: 2574348ccc0SEugene Kobyak return "write"; 2584348ccc0SEugene Kobyak case JOB_CONFIG_RW_RANDWRITE: 2594348ccc0SEugene Kobyak return "randwrite"; 2604348ccc0SEugene Kobyak case JOB_CONFIG_RW_VERIFY: 2614348ccc0SEugene Kobyak return "verify"; 2624348ccc0SEugene Kobyak case JOB_CONFIG_RW_RESET: 2634348ccc0SEugene Kobyak return "reset"; 2644348ccc0SEugene Kobyak case JOB_CONFIG_RW_UNMAP: 2654348ccc0SEugene Kobyak return "unmap"; 2664348ccc0SEugene Kobyak case JOB_CONFIG_RW_WRITE_ZEROES: 2674348ccc0SEugene Kobyak return "write_zeroes"; 2684348ccc0SEugene Kobyak case JOB_CONFIG_RW_FLUSH: 2694348ccc0SEugene Kobyak return "flush"; 2704348ccc0SEugene Kobyak case JOB_CONFIG_RW_RW: 2714348ccc0SEugene Kobyak return "rw"; 2724348ccc0SEugene Kobyak case JOB_CONFIG_RW_RANDRW: 2734348ccc0SEugene Kobyak return "randrw"; 2744348ccc0SEugene Kobyak default: 2754348ccc0SEugene Kobyak fprintf(stderr, "wrong workload_type code\n"); 2764348ccc0SEugene Kobyak } 2774348ccc0SEugene Kobyak 2784348ccc0SEugene Kobyak return NULL; 2794348ccc0SEugene Kobyak } 2804348ccc0SEugene Kobyak 28129784f35SKrzysztof Karas /* 28229784f35SKrzysztof Karas * Cumulative Moving Average (CMA): average of all data up to current 28329784f35SKrzysztof Karas * Exponential Moving Average (EMA): weighted mean of the previous n data and more weight is given to recent 28429784f35SKrzysztof Karas * Simple Moving Average (SMA): unweighted mean of the previous n data 28529784f35SKrzysztof Karas * 28629784f35SKrzysztof Karas * Bdevperf supports CMA and EMA. 28729784f35SKrzysztof Karas */ 28829784f35SKrzysztof Karas static double 28929784f35SKrzysztof Karas get_cma_io_per_second(struct bdevperf_job *job, uint64_t io_time_in_usec) 29029784f35SKrzysztof Karas { 291045c781dSRichael Zhuang return (double)job->io_completed * SPDK_SEC_TO_USEC / io_time_in_usec; 29229784f35SKrzysztof Karas } 29329784f35SKrzysztof Karas 29429784f35SKrzysztof Karas static double 29529784f35SKrzysztof Karas get_ema_io_per_second(struct bdevperf_job *job, uint64_t ema_period) 29629784f35SKrzysztof Karas { 29729784f35SKrzysztof Karas double io_completed, io_per_second; 29829784f35SKrzysztof Karas 29929784f35SKrzysztof Karas io_completed = job->io_completed; 300045c781dSRichael Zhuang io_per_second = (double)(io_completed - job->prev_io_completed) * SPDK_SEC_TO_USEC 30129784f35SKrzysztof Karas / g_show_performance_period_in_usec; 30229784f35SKrzysztof Karas job->prev_io_completed = io_completed; 30329784f35SKrzysztof Karas 30429784f35SKrzysztof Karas job->ema_io_per_second += (io_per_second - job->ema_io_per_second) * 2 30529784f35SKrzysztof Karas / (ema_period + 1); 30629784f35SKrzysztof Karas return job->ema_io_per_second; 30729784f35SKrzysztof Karas } 30829784f35SKrzysztof Karas 30929784f35SKrzysztof Karas static void 310723dd06eSRichael Zhuang get_avg_latency(void *ctx, uint64_t start, uint64_t end, uint64_t count, 311723dd06eSRichael Zhuang uint64_t total, uint64_t so_far) 312723dd06eSRichael Zhuang { 313723dd06eSRichael Zhuang struct latency_info *latency_info = ctx; 314723dd06eSRichael Zhuang 315723dd06eSRichael Zhuang if (count == 0) { 316723dd06eSRichael Zhuang return; 317723dd06eSRichael Zhuang } 318723dd06eSRichael Zhuang 319723dd06eSRichael Zhuang latency_info->total += (start + end) / 2 * count; 320723dd06eSRichael Zhuang 321723dd06eSRichael Zhuang if (so_far == count) { 322723dd06eSRichael Zhuang latency_info->min = start; 323723dd06eSRichael Zhuang } 324723dd06eSRichael Zhuang 325723dd06eSRichael Zhuang if (so_far == total) { 326723dd06eSRichael Zhuang latency_info->max = end; 327723dd06eSRichael Zhuang } 328723dd06eSRichael Zhuang } 329723dd06eSRichael Zhuang 330723dd06eSRichael Zhuang static void 33172177d39SMarcin Spiewak bdevperf_job_stats_accumulate(struct bdevperf_stats *aggr_stats, 33272177d39SMarcin Spiewak struct bdevperf_stats *job_stats) 33372177d39SMarcin Spiewak { 33472177d39SMarcin Spiewak aggr_stats->total_io_per_second += job_stats->total_io_per_second; 33572177d39SMarcin Spiewak aggr_stats->total_mb_per_second += job_stats->total_mb_per_second; 33672177d39SMarcin Spiewak aggr_stats->total_failed_per_second += job_stats->total_failed_per_second; 33772177d39SMarcin Spiewak aggr_stats->total_timeout_per_second += job_stats->total_timeout_per_second; 33872177d39SMarcin Spiewak aggr_stats->total_io_completed += job_stats->total_io_completed; 33972177d39SMarcin Spiewak aggr_stats->total_tsc += job_stats->total_tsc; 34072177d39SMarcin Spiewak 34172177d39SMarcin Spiewak if (job_stats->min_latency < aggr_stats->min_latency) { 34272177d39SMarcin Spiewak aggr_stats->min_latency = job_stats->min_latency; 34372177d39SMarcin Spiewak } 34472177d39SMarcin Spiewak if (job_stats->max_latency > aggr_stats->max_latency) { 34572177d39SMarcin Spiewak aggr_stats->max_latency = job_stats->max_latency; 34672177d39SMarcin Spiewak } 34772177d39SMarcin Spiewak } 34872177d39SMarcin Spiewak 34972177d39SMarcin Spiewak static void 35072177d39SMarcin Spiewak bdevperf_job_get_stats(struct bdevperf_job *job, 35172177d39SMarcin Spiewak struct bdevperf_stats *job_stats, 35272177d39SMarcin Spiewak uint64_t time_in_usec, 35372177d39SMarcin Spiewak uint64_t ema_period) 35429784f35SKrzysztof Karas { 35529784f35SKrzysztof Karas double io_per_second, mb_per_second, failed_per_second, timeout_per_second; 356723dd06eSRichael Zhuang double average_latency = 0.0, min_latency, max_latency; 357723dd06eSRichael Zhuang uint64_t tsc_rate; 358723dd06eSRichael Zhuang uint64_t total_io; 359723dd06eSRichael Zhuang struct latency_info latency_info = {}; 36029784f35SKrzysztof Karas 36172177d39SMarcin Spiewak if (ema_period == 0) { 36229784f35SKrzysztof Karas io_per_second = get_cma_io_per_second(job, time_in_usec); 36329784f35SKrzysztof Karas } else { 36472177d39SMarcin Spiewak io_per_second = get_ema_io_per_second(job, ema_period); 36529784f35SKrzysztof Karas } 366723dd06eSRichael Zhuang tsc_rate = spdk_get_ticks_hz(); 36729784f35SKrzysztof Karas mb_per_second = io_per_second * job->io_size / (1024 * 1024); 36829784f35SKrzysztof Karas 369723dd06eSRichael Zhuang spdk_histogram_data_iterate(job->histogram, get_avg_latency, &latency_info); 370723dd06eSRichael Zhuang 371723dd06eSRichael Zhuang total_io = job->io_completed + job->io_failed; 372723dd06eSRichael Zhuang if (total_io != 0) { 373045c781dSRichael Zhuang average_latency = (double)latency_info.total / total_io * SPDK_SEC_TO_USEC / tsc_rate; 374723dd06eSRichael Zhuang } 375045c781dSRichael Zhuang min_latency = (double)latency_info.min * SPDK_SEC_TO_USEC / tsc_rate; 376045c781dSRichael Zhuang max_latency = (double)latency_info.max * SPDK_SEC_TO_USEC / tsc_rate; 377723dd06eSRichael Zhuang 378045c781dSRichael Zhuang failed_per_second = (double)job->io_failed * SPDK_SEC_TO_USEC / time_in_usec; 379045c781dSRichael Zhuang timeout_per_second = (double)job->io_timeout * SPDK_SEC_TO_USEC / time_in_usec; 38029784f35SKrzysztof Karas 38172177d39SMarcin Spiewak job_stats->total_io_per_second = io_per_second; 38272177d39SMarcin Spiewak job_stats->total_mb_per_second = mb_per_second; 38372177d39SMarcin Spiewak job_stats->total_failed_per_second = failed_per_second; 38472177d39SMarcin Spiewak job_stats->total_timeout_per_second = timeout_per_second; 38572177d39SMarcin Spiewak job_stats->total_io_completed = total_io; 38672177d39SMarcin Spiewak job_stats->total_tsc = latency_info.total; 38772177d39SMarcin Spiewak job_stats->average_latency = average_latency; 38872177d39SMarcin Spiewak job_stats->min_latency = min_latency; 38972177d39SMarcin Spiewak job_stats->max_latency = max_latency; 39072177d39SMarcin Spiewak job_stats->io_time_in_usec = time_in_usec; 39172177d39SMarcin Spiewak } 39272177d39SMarcin Spiewak 39372177d39SMarcin Spiewak static void 39472177d39SMarcin Spiewak performance_dump_job_stdout(struct bdevperf_job *job, 39572177d39SMarcin Spiewak struct bdevperf_stats *job_stats) 39672177d39SMarcin Spiewak { 39727134348SAnisa Su if (job->workload_type == JOB_CONFIG_RW_RW || job->workload_type == JOB_CONFIG_RW_RANDRW) { 39827134348SAnisa Su printf("Job: %s (Core Mask 0x%s, workload: %s, percentage: %d, depth: %d, IO size: %d)\n", 39927134348SAnisa Su job->name, spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread)), 40027134348SAnisa Su parse_workload_type(job->workload_type), job->rw_percentage, 40127134348SAnisa Su job->queue_depth, job->io_size); 40227134348SAnisa Su } else { 40327134348SAnisa Su printf("Job: %s (Core Mask 0x%s, workload: %s, depth: %d, IO size: %d)\n", 40427134348SAnisa Su job->name, spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread)), 40527134348SAnisa Su parse_workload_type(job->workload_type), job->queue_depth, job->io_size); 40627134348SAnisa Su } 40727134348SAnisa Su 40827134348SAnisa Su 40927134348SAnisa Su if (job->io_failed > 0 && !job->reset && !job->continue_on_failure) { 41027134348SAnisa Su printf("Job: %s ended in about %.2f seconds with error\n", 41127134348SAnisa Su job->name, (double)job->run_time_in_usec / SPDK_SEC_TO_USEC); 41227134348SAnisa Su } 41327134348SAnisa Su if (job->verify) { 41427134348SAnisa Su printf("\t Verification LBA range: start 0x%" PRIx64 " length 0x%" PRIx64 "\n", 41527134348SAnisa Su job->ios_base, job->size_in_ios); 41627134348SAnisa Su } 41727134348SAnisa Su 41829784f35SKrzysztof Karas printf("\t %-20s: %10.2f %10.2f %10.2f", 41972177d39SMarcin Spiewak job->name, 42072177d39SMarcin Spiewak (float)job_stats->io_time_in_usec / SPDK_SEC_TO_USEC, 42172177d39SMarcin Spiewak job_stats->total_io_per_second, 42272177d39SMarcin Spiewak job_stats->total_mb_per_second); 423723dd06eSRichael Zhuang printf(" %10.2f %8.2f", 42472177d39SMarcin Spiewak job_stats->total_failed_per_second, 42572177d39SMarcin Spiewak job_stats->total_timeout_per_second); 426723dd06eSRichael Zhuang printf(" %10.2f %10.2f %10.2f\n", 42772177d39SMarcin Spiewak job_stats->average_latency, 42872177d39SMarcin Spiewak job_stats->min_latency, 42972177d39SMarcin Spiewak job_stats->max_latency); 43029784f35SKrzysztof Karas } 43129784f35SKrzysztof Karas 43229784f35SKrzysztof Karas static void 4333a141b72SMarcin Spiewak performance_dump_job_json(struct bdevperf_job *job, 4343a141b72SMarcin Spiewak struct spdk_json_write_ctx *w, 4353a141b72SMarcin Spiewak struct bdevperf_stats *job_stats) 4363a141b72SMarcin Spiewak { 4373a141b72SMarcin Spiewak char core_mask_string[BDEVPERF_MAX_COREMASK_STRING] = {0}; 4383a141b72SMarcin Spiewak 4393a141b72SMarcin Spiewak spdk_json_write_named_string(w, "job", job->name); 4403a141b72SMarcin Spiewak snprintf(core_mask_string, BDEVPERF_MAX_COREMASK_STRING, 4413a141b72SMarcin Spiewak "0x%s", spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread))); 4423a141b72SMarcin Spiewak spdk_json_write_named_string(w, "core_mask", core_mask_string); 4433a141b72SMarcin Spiewak spdk_json_write_named_string(w, "workload", parse_workload_type(job->workload_type)); 4443a141b72SMarcin Spiewak 4453a141b72SMarcin Spiewak if (job->workload_type == JOB_CONFIG_RW_RW || job->workload_type == JOB_CONFIG_RW_RANDRW) { 4463a141b72SMarcin Spiewak spdk_json_write_named_uint32(w, "percentage", job->rw_percentage); 4473a141b72SMarcin Spiewak } 4483a141b72SMarcin Spiewak 4493a141b72SMarcin Spiewak if (g_shutdown) { 4503a141b72SMarcin Spiewak spdk_json_write_named_string(w, "status", "terminated"); 4513a141b72SMarcin Spiewak } else if (job->io_failed > 0 && !job->reset && !job->continue_on_failure) { 4523a141b72SMarcin Spiewak spdk_json_write_named_string(w, "status", "failed"); 4533a141b72SMarcin Spiewak } else { 4543a141b72SMarcin Spiewak spdk_json_write_named_string(w, "status", "finished"); 4553a141b72SMarcin Spiewak } 4563a141b72SMarcin Spiewak 4573a141b72SMarcin Spiewak if (job->verify) { 4583a141b72SMarcin Spiewak spdk_json_write_named_object_begin(w, "verify_range"); 4593a141b72SMarcin Spiewak spdk_json_write_named_uint64(w, "start", job->ios_base); 4603a141b72SMarcin Spiewak spdk_json_write_named_uint64(w, "length", job->size_in_ios); 4613a141b72SMarcin Spiewak spdk_json_write_object_end(w); 4623a141b72SMarcin Spiewak } 4633a141b72SMarcin Spiewak 4643a141b72SMarcin Spiewak spdk_json_write_named_uint32(w, "queue_depth", job->queue_depth); 4653a141b72SMarcin Spiewak spdk_json_write_named_uint32(w, "io_size", job->io_size); 4663a141b72SMarcin Spiewak spdk_json_write_named_double(w, "runtime", (double)job_stats->io_time_in_usec / SPDK_SEC_TO_USEC); 4673a141b72SMarcin Spiewak spdk_json_write_named_double(w, "iops", job_stats->total_io_per_second); 4683a141b72SMarcin Spiewak spdk_json_write_named_double(w, "mibps", job_stats->total_mb_per_second); 4693a141b72SMarcin Spiewak spdk_json_write_named_uint64(w, "io_failed", job->io_failed); 4703a141b72SMarcin Spiewak spdk_json_write_named_uint64(w, "io_timeout", job->io_timeout); 4713a141b72SMarcin Spiewak spdk_json_write_named_double(w, "avg_latency_us", job_stats->average_latency); 4723a141b72SMarcin Spiewak spdk_json_write_named_double(w, "min_latency_us", job_stats->min_latency); 4733a141b72SMarcin Spiewak spdk_json_write_named_double(w, "max_latency_us", job_stats->max_latency); 4743a141b72SMarcin Spiewak } 4753a141b72SMarcin Spiewak 4763a141b72SMarcin Spiewak static void 4773353236fSArtur Paszkiewicz generate_data(struct bdevperf_job *job, void *buf, void *md_buf, bool unique) 47829784f35SKrzysztof Karas { 47929784f35SKrzysztof Karas int offset_blocks = 0, md_offset, data_block_size, inner_offset; 4803353236fSArtur Paszkiewicz int buf_len = job->buf_size; 481894d5af2SShuhei Matsumoto int block_size = spdk_bdev_desc_get_block_size(job->bdev_desc); 482894d5af2SShuhei Matsumoto int md_size = spdk_bdev_desc_get_md_size(job->bdev_desc); 4833353236fSArtur Paszkiewicz int num_blocks = job->io_size_blocks; 48429784f35SKrzysztof Karas 48529784f35SKrzysztof Karas if (buf_len < num_blocks * block_size) { 48629784f35SKrzysztof Karas return; 48729784f35SKrzysztof Karas } 48829784f35SKrzysztof Karas 48929784f35SKrzysztof Karas if (md_buf == NULL) { 49029784f35SKrzysztof Karas data_block_size = block_size - md_size; 49129784f35SKrzysztof Karas md_buf = (char *)buf + data_block_size; 49229784f35SKrzysztof Karas md_offset = block_size; 49329784f35SKrzysztof Karas } else { 49429784f35SKrzysztof Karas data_block_size = block_size; 49529784f35SKrzysztof Karas md_offset = md_size; 49629784f35SKrzysztof Karas } 49729784f35SKrzysztof Karas 4983353236fSArtur Paszkiewicz if (unique) { 4993353236fSArtur Paszkiewicz uint64_t io_count = job->write_io_count++; 5003353236fSArtur Paszkiewicz unsigned int i; 5013353236fSArtur Paszkiewicz 5023353236fSArtur Paszkiewicz assert(md_size == 0 || md_size >= (int)sizeof(uint64_t)); 5033353236fSArtur Paszkiewicz 5043353236fSArtur Paszkiewicz while (offset_blocks < num_blocks) { 5053353236fSArtur Paszkiewicz inner_offset = 0; 5063353236fSArtur Paszkiewicz while (inner_offset < data_block_size) { 5073353236fSArtur Paszkiewicz *(uint64_t *)buf = (io_count << 32) | (offset_blocks + inner_offset); 5083353236fSArtur Paszkiewicz inner_offset += sizeof(uint64_t); 5093353236fSArtur Paszkiewicz buf += sizeof(uint64_t); 5103353236fSArtur Paszkiewicz } 5113353236fSArtur Paszkiewicz for (i = 0; i < md_size / sizeof(uint64_t); i++) { 5123353236fSArtur Paszkiewicz ((uint64_t *)md_buf)[i] = (io_count << 32) | offset_blocks; 5133353236fSArtur Paszkiewicz } 5143353236fSArtur Paszkiewicz md_buf += md_offset; 5153353236fSArtur Paszkiewicz offset_blocks++; 5163353236fSArtur Paszkiewicz } 5173353236fSArtur Paszkiewicz return; 5183353236fSArtur Paszkiewicz } 5193353236fSArtur Paszkiewicz 52029784f35SKrzysztof Karas while (offset_blocks < num_blocks) { 52129784f35SKrzysztof Karas inner_offset = 0; 52229784f35SKrzysztof Karas while (inner_offset < data_block_size) { 52329784f35SKrzysztof Karas *(uint32_t *)buf = offset_blocks + inner_offset; 52429784f35SKrzysztof Karas inner_offset += sizeof(uint32_t); 52529784f35SKrzysztof Karas buf += sizeof(uint32_t); 52629784f35SKrzysztof Karas } 52729784f35SKrzysztof Karas memset(md_buf, offset_blocks, md_size); 52829784f35SKrzysztof Karas md_buf += md_offset; 52929784f35SKrzysztof Karas offset_blocks++; 53029784f35SKrzysztof Karas } 53129784f35SKrzysztof Karas } 53229784f35SKrzysztof Karas 53329784f35SKrzysztof Karas static bool 53429784f35SKrzysztof Karas copy_data(void *wr_buf, int wr_buf_len, void *rd_buf, int rd_buf_len, int block_size, 53529784f35SKrzysztof Karas void *wr_md_buf, void *rd_md_buf, int md_size, int num_blocks) 53629784f35SKrzysztof Karas { 53729784f35SKrzysztof Karas if (wr_buf_len < num_blocks * block_size || rd_buf_len < num_blocks * block_size) { 53829784f35SKrzysztof Karas return false; 53929784f35SKrzysztof Karas } 54029784f35SKrzysztof Karas 54129784f35SKrzysztof Karas assert((wr_md_buf != NULL) == (rd_md_buf != NULL)); 54229784f35SKrzysztof Karas 54329784f35SKrzysztof Karas memcpy(wr_buf, rd_buf, block_size * num_blocks); 54429784f35SKrzysztof Karas 54529784f35SKrzysztof Karas if (wr_md_buf != NULL) { 54629784f35SKrzysztof Karas memcpy(wr_md_buf, rd_md_buf, md_size * num_blocks); 54729784f35SKrzysztof Karas } 54829784f35SKrzysztof Karas 54929784f35SKrzysztof Karas return true; 55029784f35SKrzysztof Karas } 55129784f35SKrzysztof Karas 55229784f35SKrzysztof Karas static bool 55329784f35SKrzysztof Karas verify_data(void *wr_buf, int wr_buf_len, void *rd_buf, int rd_buf_len, int block_size, 55429784f35SKrzysztof Karas void *wr_md_buf, void *rd_md_buf, int md_size, int num_blocks, bool md_check) 55529784f35SKrzysztof Karas { 55629784f35SKrzysztof Karas int offset_blocks = 0, md_offset, data_block_size; 55729784f35SKrzysztof Karas 55829784f35SKrzysztof Karas if (wr_buf_len < num_blocks * block_size || rd_buf_len < num_blocks * block_size) { 55929784f35SKrzysztof Karas return false; 56029784f35SKrzysztof Karas } 56129784f35SKrzysztof Karas 56229784f35SKrzysztof Karas assert((wr_md_buf != NULL) == (rd_md_buf != NULL)); 56329784f35SKrzysztof Karas 56429784f35SKrzysztof Karas if (wr_md_buf == NULL) { 56529784f35SKrzysztof Karas data_block_size = block_size - md_size; 56629784f35SKrzysztof Karas wr_md_buf = (char *)wr_buf + data_block_size; 56729784f35SKrzysztof Karas rd_md_buf = (char *)rd_buf + data_block_size; 56829784f35SKrzysztof Karas md_offset = block_size; 56929784f35SKrzysztof Karas } else { 57029784f35SKrzysztof Karas data_block_size = block_size; 57129784f35SKrzysztof Karas md_offset = md_size; 57229784f35SKrzysztof Karas } 57329784f35SKrzysztof Karas 57429784f35SKrzysztof Karas while (offset_blocks < num_blocks) { 57529784f35SKrzysztof Karas if (memcmp(wr_buf, rd_buf, data_block_size) != 0) { 576c7c25930SJacek Kalwas printf("data_block_size %d, num_blocks %d, offset %d\n", data_block_size, num_blocks, 577c7c25930SJacek Kalwas offset_blocks); 578c7c25930SJacek Kalwas spdk_log_dump(stdout, "rd_buf", rd_buf, data_block_size); 579c7c25930SJacek Kalwas spdk_log_dump(stdout, "wr_buf", wr_buf, data_block_size); 58029784f35SKrzysztof Karas return false; 58129784f35SKrzysztof Karas } 58229784f35SKrzysztof Karas 58329784f35SKrzysztof Karas wr_buf += block_size; 58429784f35SKrzysztof Karas rd_buf += block_size; 58529784f35SKrzysztof Karas 58629784f35SKrzysztof Karas if (md_check) { 58729784f35SKrzysztof Karas if (memcmp(wr_md_buf, rd_md_buf, md_size) != 0) { 588c7c25930SJacek Kalwas printf("md_size %d, num_blocks %d, offset %d\n", md_size, num_blocks, offset_blocks); 589c7c25930SJacek Kalwas spdk_log_dump(stdout, "rd_md_buf", rd_md_buf, md_size); 590c7c25930SJacek Kalwas spdk_log_dump(stdout, "wr_md_buf", wr_md_buf, md_size); 59129784f35SKrzysztof Karas return false; 59229784f35SKrzysztof Karas } 59329784f35SKrzysztof Karas 59429784f35SKrzysztof Karas wr_md_buf += md_offset; 59529784f35SKrzysztof Karas rd_md_buf += md_offset; 59629784f35SKrzysztof Karas } 59729784f35SKrzysztof Karas 59829784f35SKrzysztof Karas offset_blocks++; 59929784f35SKrzysztof Karas } 60029784f35SKrzysztof Karas 60129784f35SKrzysztof Karas return true; 60229784f35SKrzysztof Karas } 60329784f35SKrzysztof Karas 60429784f35SKrzysztof Karas static void 60529784f35SKrzysztof Karas free_job_config(void) 60629784f35SKrzysztof Karas { 60729784f35SKrzysztof Karas struct job_config *config, *tmp; 60829784f35SKrzysztof Karas 60929784f35SKrzysztof Karas spdk_conf_free(g_bdevperf_conf); 61029784f35SKrzysztof Karas g_bdevperf_conf = NULL; 61129784f35SKrzysztof Karas 61229784f35SKrzysztof Karas TAILQ_FOREACH_SAFE(config, &job_config_list, link, tmp) { 61329784f35SKrzysztof Karas TAILQ_REMOVE(&job_config_list, config, link); 61429784f35SKrzysztof Karas free(config); 61529784f35SKrzysztof Karas } 61629784f35SKrzysztof Karas } 61729784f35SKrzysztof Karas 61829784f35SKrzysztof Karas static void 619dcb296a3SJim Harris bdevperf_job_free(struct bdevperf_job *job) 620dcb296a3SJim Harris { 6217cc16c96SShuhei Matsumoto if (job->bdev_desc != NULL) { 6227cc16c96SShuhei Matsumoto spdk_bdev_close(job->bdev_desc); 6237cc16c96SShuhei Matsumoto } 6247cc16c96SShuhei Matsumoto 625723dd06eSRichael Zhuang spdk_histogram_data_free(job->histogram); 626dcb296a3SJim Harris spdk_bit_array_free(&job->outstanding); 627d254a3b9SSlawomir Ptak spdk_bit_array_free(&job->random_map); 628dcb296a3SJim Harris spdk_zipf_free(&job->zipf); 629dcb296a3SJim Harris free(job->name); 630dcb296a3SJim Harris free(job); 631dcb296a3SJim Harris } 632dcb296a3SJim Harris 633dcb296a3SJim Harris static void 634081b190bSJim Harris job_thread_exit(void *ctx) 635081b190bSJim Harris { 636081b190bSJim Harris spdk_thread_exit(spdk_get_thread()); 637081b190bSJim Harris } 638081b190bSJim Harris 639081b190bSJim Harris static void 640723dd06eSRichael Zhuang check_cutoff(void *ctx, uint64_t start, uint64_t end, uint64_t count, 641723dd06eSRichael Zhuang uint64_t total, uint64_t so_far) 642723dd06eSRichael Zhuang { 643723dd06eSRichael Zhuang double so_far_pct; 644723dd06eSRichael Zhuang double **cutoff = ctx; 645723dd06eSRichael Zhuang uint64_t tsc_rate; 646723dd06eSRichael Zhuang 647723dd06eSRichael Zhuang if (count == 0) { 648723dd06eSRichael Zhuang return; 649723dd06eSRichael Zhuang } 650723dd06eSRichael Zhuang 651723dd06eSRichael Zhuang tsc_rate = spdk_get_ticks_hz(); 652723dd06eSRichael Zhuang so_far_pct = (double)so_far / total; 653723dd06eSRichael Zhuang while (so_far_pct >= **cutoff && **cutoff > 0) { 654045c781dSRichael Zhuang printf("%9.5f%% : %9.3fus\n", **cutoff * 100, (double)end * SPDK_SEC_TO_USEC / tsc_rate); 655723dd06eSRichael Zhuang (*cutoff)++; 656723dd06eSRichael Zhuang } 657723dd06eSRichael Zhuang } 658723dd06eSRichael Zhuang 659723dd06eSRichael Zhuang static void 660723dd06eSRichael Zhuang print_bucket(void *ctx, uint64_t start, uint64_t end, uint64_t count, 661723dd06eSRichael Zhuang uint64_t total, uint64_t so_far) 662723dd06eSRichael Zhuang { 663723dd06eSRichael Zhuang double so_far_pct; 664723dd06eSRichael Zhuang uint64_t tsc_rate; 665723dd06eSRichael Zhuang 666723dd06eSRichael Zhuang if (count == 0) { 667723dd06eSRichael Zhuang return; 668723dd06eSRichael Zhuang } 669723dd06eSRichael Zhuang 670723dd06eSRichael Zhuang tsc_rate = spdk_get_ticks_hz(); 671723dd06eSRichael Zhuang so_far_pct = (double)so_far * 100 / total; 672723dd06eSRichael Zhuang printf("%9.3f - %9.3f: %9.4f%% (%9ju)\n", 673045c781dSRichael Zhuang (double)start * SPDK_SEC_TO_USEC / tsc_rate, 674045c781dSRichael Zhuang (double)end * SPDK_SEC_TO_USEC / tsc_rate, 675723dd06eSRichael Zhuang so_far_pct, count); 676723dd06eSRichael Zhuang } 677723dd06eSRichael Zhuang 678723dd06eSRichael Zhuang static void 67929784f35SKrzysztof Karas bdevperf_test_done(void *ctx) 68029784f35SKrzysztof Karas { 68129784f35SKrzysztof Karas struct bdevperf_job *job, *jtmp; 68229784f35SKrzysztof Karas struct bdevperf_task *task, *ttmp; 6838e373044SShuhei Matsumoto struct lcore_thread *lthread, *lttmp; 684723dd06eSRichael Zhuang double average_latency = 0.0; 68529784f35SKrzysztof Karas uint64_t time_in_usec; 686723dd06eSRichael Zhuang int rc; 6873a141b72SMarcin Spiewak struct spdk_json_write_ctx *w = NULL; 68872177d39SMarcin Spiewak struct bdevperf_stats job_stats = {0}; 6893a141b72SMarcin Spiewak struct spdk_cpuset cpu_mask; 69029784f35SKrzysztof Karas 69129784f35SKrzysztof Karas if (g_time_in_usec) { 692c273168dSMarcin Spiewak g_stats.total.io_time_in_usec = g_time_in_usec; 69329784f35SKrzysztof Karas 69429784f35SKrzysztof Karas if (!g_run_rc && g_performance_dump_active) { 69529784f35SKrzysztof Karas spdk_thread_send_msg(spdk_get_thread(), bdevperf_test_done, NULL); 69629784f35SKrzysztof Karas return; 69729784f35SKrzysztof Karas } 69829784f35SKrzysztof Karas } 69929784f35SKrzysztof Karas 70029784f35SKrzysztof Karas spdk_poller_unregister(&g_perf_timer); 70129784f35SKrzysztof Karas 70229784f35SKrzysztof Karas if (g_shutdown) { 70329784f35SKrzysztof Karas g_shutdown_tsc = spdk_get_ticks() - g_start_tsc; 704045c781dSRichael Zhuang time_in_usec = g_shutdown_tsc * SPDK_SEC_TO_USEC / spdk_get_ticks_hz(); 70529784f35SKrzysztof Karas g_time_in_usec = (g_time_in_usec > time_in_usec) ? time_in_usec : g_time_in_usec; 70629784f35SKrzysztof Karas printf("Received shutdown signal, test time was about %.6f seconds\n", 707045c781dSRichael Zhuang (double)g_time_in_usec / SPDK_SEC_TO_USEC); 70829784f35SKrzysztof Karas } 7093a141b72SMarcin Spiewak /* Send RPC response if g_run_rc indicate success, or shutdown request was sent to bdevperf. 7103a141b72SMarcin Spiewak * rpc_perform_tests_cb will send error response in case of error. 7113a141b72SMarcin Spiewak */ 7123a141b72SMarcin Spiewak if ((g_run_rc == 0 || g_shutdown) && g_request) { 7133a141b72SMarcin Spiewak w = spdk_jsonrpc_begin_result(g_request); 7143a141b72SMarcin Spiewak spdk_json_write_object_begin(w); 7153a141b72SMarcin Spiewak spdk_json_write_named_array_begin(w, "results"); 7163a141b72SMarcin Spiewak } 71729784f35SKrzysztof Karas 718723dd06eSRichael Zhuang printf("\n%*s\n", 107, "Latency(us)"); 719723dd06eSRichael Zhuang printf("\r %-*s: %10s %10s %10s %10s %8s %10s %10s %10s\n", 720723dd06eSRichael Zhuang 28, "Device Information", "runtime(s)", "IOPS", "MiB/s", "Fail/s", "TO/s", "Average", "min", "max"); 72129784f35SKrzysztof Karas 7223a141b72SMarcin Spiewak 7233a141b72SMarcin Spiewak spdk_cpuset_zero(&cpu_mask); 72429784f35SKrzysztof Karas TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) { 7253a141b72SMarcin Spiewak spdk_cpuset_or(&cpu_mask, spdk_thread_get_cpumask(job->thread)); 72672177d39SMarcin Spiewak memset(&job_stats, 0, sizeof(job_stats)); 72772177d39SMarcin Spiewak bdevperf_job_get_stats(job, &job_stats, job->run_time_in_usec, 0); 72872177d39SMarcin Spiewak bdevperf_job_stats_accumulate(&g_stats.total, &job_stats); 72972177d39SMarcin Spiewak performance_dump_job_stdout(job, &job_stats); 7303a141b72SMarcin Spiewak if (w) { 7313a141b72SMarcin Spiewak spdk_json_write_object_begin(w); 7323a141b72SMarcin Spiewak performance_dump_job_json(job, w, &job_stats); 7333a141b72SMarcin Spiewak spdk_json_write_object_end(w); 7343a141b72SMarcin Spiewak } 735723dd06eSRichael Zhuang } 736723dd06eSRichael Zhuang 7373a141b72SMarcin Spiewak if (w) { 7383a141b72SMarcin Spiewak spdk_json_write_array_end(w); 7393a141b72SMarcin Spiewak spdk_json_write_named_uint32(w, "core_count", spdk_cpuset_count(&cpu_mask)); 7403a141b72SMarcin Spiewak spdk_json_write_object_end(w); 7413a141b72SMarcin Spiewak spdk_jsonrpc_end_result(g_request, w); 7423a141b72SMarcin Spiewak } 743723dd06eSRichael Zhuang printf("\r ==================================================================================" 744723dd06eSRichael Zhuang "=================================\n"); 745723dd06eSRichael Zhuang printf("\r %-28s: %10s %10.2f %10.2f", 746c273168dSMarcin Spiewak "Total", "", g_stats.total.total_io_per_second, g_stats.total.total_mb_per_second); 747723dd06eSRichael Zhuang printf(" %10.2f %8.2f", 748c273168dSMarcin Spiewak g_stats.total.total_failed_per_second, g_stats.total.total_timeout_per_second); 749723dd06eSRichael Zhuang 750c273168dSMarcin Spiewak if (g_stats.total.total_io_completed != 0) { 751c273168dSMarcin Spiewak average_latency = ((double)g_stats.total.total_tsc / g_stats.total.total_io_completed) * 752c273168dSMarcin Spiewak SPDK_SEC_TO_USEC / 753723dd06eSRichael Zhuang spdk_get_ticks_hz(); 754723dd06eSRichael Zhuang } 755c273168dSMarcin Spiewak printf(" %10.2f %10.2f %10.2f\n", average_latency, g_stats.total.min_latency, 756c273168dSMarcin Spiewak g_stats.total.max_latency); 757723dd06eSRichael Zhuang 758c273168dSMarcin Spiewak if (g_latency_display_level == 0 || g_stats.total.total_io_completed == 0) { 759723dd06eSRichael Zhuang goto clean; 760723dd06eSRichael Zhuang } 761723dd06eSRichael Zhuang 762723dd06eSRichael Zhuang printf("\n Latency summary\n"); 763723dd06eSRichael Zhuang TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) { 764723dd06eSRichael Zhuang printf("\r =============================================\n"); 76572da547aSShuhei Matsumoto printf("\r Job: %s (Core Mask 0x%s)\n", job->name, 766723dd06eSRichael Zhuang spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread))); 767723dd06eSRichael Zhuang 768723dd06eSRichael Zhuang const double *cutoff = g_latency_cutoffs; 769723dd06eSRichael Zhuang 770723dd06eSRichael Zhuang spdk_histogram_data_iterate(job->histogram, check_cutoff, &cutoff); 771723dd06eSRichael Zhuang 772723dd06eSRichael Zhuang printf("\n"); 773723dd06eSRichael Zhuang } 774723dd06eSRichael Zhuang 775723dd06eSRichael Zhuang if (g_latency_display_level == 1) { 776723dd06eSRichael Zhuang goto clean; 777723dd06eSRichael Zhuang } 778723dd06eSRichael Zhuang 779723dd06eSRichael Zhuang printf("\r Latency histogram\n"); 780723dd06eSRichael Zhuang TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) { 781723dd06eSRichael Zhuang printf("\r =============================================\n"); 78272da547aSShuhei Matsumoto printf("\r Job: %s (Core Mask 0x%s)\n", job->name, 783723dd06eSRichael Zhuang spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread))); 784723dd06eSRichael Zhuang 785723dd06eSRichael Zhuang spdk_histogram_data_iterate(job->histogram, print_bucket, NULL); 786723dd06eSRichael Zhuang printf("\n"); 787723dd06eSRichael Zhuang } 788723dd06eSRichael Zhuang 789723dd06eSRichael Zhuang clean: 79021828e4aSWojciech Panfil fflush(stdout); 79121828e4aSWojciech Panfil 792723dd06eSRichael Zhuang TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) { 793723dd06eSRichael Zhuang TAILQ_REMOVE(&g_bdevperf.jobs, job, link); 79429784f35SKrzysztof Karas 7958e373044SShuhei Matsumoto if (!g_one_thread_per_lcore) { 796081b190bSJim Harris spdk_thread_send_msg(job->thread, job_thread_exit, NULL); 7978e373044SShuhei Matsumoto } 798081b190bSJim Harris 79929784f35SKrzysztof Karas TAILQ_FOREACH_SAFE(task, &job->task_list, link, ttmp) { 80029784f35SKrzysztof Karas TAILQ_REMOVE(&job->task_list, task, link); 80129784f35SKrzysztof Karas spdk_free(task->buf); 802aa824ae6SOlivier Mayer spdk_free(task->verify_buf); 80329784f35SKrzysztof Karas spdk_free(task->md_buf); 804dc3ea9d2SSlawomir Ptak spdk_free(task->verify_md_buf); 80529784f35SKrzysztof Karas free(task); 80629784f35SKrzysztof Karas } 80729784f35SKrzysztof Karas 808dcb296a3SJim Harris bdevperf_job_free(job); 80929784f35SKrzysztof Karas } 81029784f35SKrzysztof Karas 8118e373044SShuhei Matsumoto if (g_one_thread_per_lcore) { 8128e373044SShuhei Matsumoto TAILQ_FOREACH_SAFE(lthread, &g_lcore_thread_list, link, lttmp) { 8138e373044SShuhei Matsumoto TAILQ_REMOVE(&g_lcore_thread_list, lthread, link); 8148e373044SShuhei Matsumoto spdk_thread_send_msg(lthread->thread, job_thread_exit, NULL); 8158e373044SShuhei Matsumoto free(lthread); 8168e373044SShuhei Matsumoto } 8178e373044SShuhei Matsumoto } 8188e373044SShuhei Matsumoto 8190bfa2536SKonrad Sztyber if (g_bdevperf_conf == NULL) { 8200bfa2536SKonrad Sztyber free_job_config(); 8210bfa2536SKonrad Sztyber } 8220bfa2536SKonrad Sztyber 82329784f35SKrzysztof Karas rc = g_run_rc; 82429784f35SKrzysztof Karas if (g_request && !g_shutdown) { 82529784f35SKrzysztof Karas rpc_perform_tests_cb(); 82629784f35SKrzysztof Karas if (rc != 0) { 82729784f35SKrzysztof Karas spdk_app_stop(rc); 82829784f35SKrzysztof Karas } 82929784f35SKrzysztof Karas } else { 83029784f35SKrzysztof Karas spdk_app_stop(rc); 83129784f35SKrzysztof Karas } 83229784f35SKrzysztof Karas } 83329784f35SKrzysztof Karas 83429784f35SKrzysztof Karas static void 83529784f35SKrzysztof Karas bdevperf_job_end(void *ctx) 83629784f35SKrzysztof Karas { 8377cc16c96SShuhei Matsumoto struct bdevperf_job *job = ctx; 8387cc16c96SShuhei Matsumoto 83929784f35SKrzysztof Karas assert(g_main_thread == spdk_get_thread()); 84029784f35SKrzysztof Karas 8417cc16c96SShuhei Matsumoto if (job->bdev_desc != NULL) { 8427cc16c96SShuhei Matsumoto spdk_bdev_close(job->bdev_desc); 8437cc16c96SShuhei Matsumoto job->bdev_desc = NULL; 8447cc16c96SShuhei Matsumoto } 8457cc16c96SShuhei Matsumoto 84629784f35SKrzysztof Karas if (--g_bdevperf.running_jobs == 0) { 84729784f35SKrzysztof Karas bdevperf_test_done(NULL); 84829784f35SKrzysztof Karas } 84929784f35SKrzysztof Karas } 85029784f35SKrzysztof Karas 85129784f35SKrzysztof Karas static void 852723dd06eSRichael Zhuang bdevperf_channel_get_histogram_cb(void *cb_arg, int status, struct spdk_histogram_data *histogram) 853723dd06eSRichael Zhuang { 854723dd06eSRichael Zhuang struct spdk_histogram_data *job_hist = cb_arg; 85560fd5c55SGangCao 85660fd5c55SGangCao if (status == 0) { 857723dd06eSRichael Zhuang spdk_histogram_data_merge(job_hist, histogram); 858723dd06eSRichael Zhuang } 85960fd5c55SGangCao } 860723dd06eSRichael Zhuang 861723dd06eSRichael Zhuang static void 862893aaaccSAlexey Marchuk bdevperf_job_empty(struct bdevperf_job *job) 863893aaaccSAlexey Marchuk { 864893aaaccSAlexey Marchuk uint64_t end_tsc = 0; 865893aaaccSAlexey Marchuk 866893aaaccSAlexey Marchuk end_tsc = spdk_get_ticks() - g_start_tsc; 867893aaaccSAlexey Marchuk job->run_time_in_usec = end_tsc * SPDK_SEC_TO_USEC / spdk_get_ticks_hz(); 868893aaaccSAlexey Marchuk /* keep histogram info before channel is destroyed */ 869893aaaccSAlexey Marchuk spdk_bdev_channel_get_histogram(job->ch, bdevperf_channel_get_histogram_cb, 870893aaaccSAlexey Marchuk job->histogram); 871893aaaccSAlexey Marchuk spdk_put_io_channel(job->ch); 8727cc16c96SShuhei Matsumoto spdk_thread_send_msg(g_main_thread, bdevperf_job_end, job); 873893aaaccSAlexey Marchuk } 874893aaaccSAlexey Marchuk 875893aaaccSAlexey Marchuk static void 87629784f35SKrzysztof Karas bdevperf_end_task(struct bdevperf_task *task) 87729784f35SKrzysztof Karas { 87829784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 87929784f35SKrzysztof Karas 88029784f35SKrzysztof Karas TAILQ_INSERT_TAIL(&job->task_list, task, link); 88129784f35SKrzysztof Karas if (job->is_draining) { 88229784f35SKrzysztof Karas if (job->current_queue_depth == 0) { 883893aaaccSAlexey Marchuk bdevperf_job_empty(job); 88429784f35SKrzysztof Karas } 88529784f35SKrzysztof Karas } 88629784f35SKrzysztof Karas } 88729784f35SKrzysztof Karas 88829784f35SKrzysztof Karas static void 88929784f35SKrzysztof Karas bdevperf_queue_io_wait_with_cb(struct bdevperf_task *task, spdk_bdev_io_wait_cb cb_fn) 89029784f35SKrzysztof Karas { 89129784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 89229784f35SKrzysztof Karas 89329784f35SKrzysztof Karas task->bdev_io_wait.bdev = job->bdev; 89429784f35SKrzysztof Karas task->bdev_io_wait.cb_fn = cb_fn; 89529784f35SKrzysztof Karas task->bdev_io_wait.cb_arg = task; 89629784f35SKrzysztof Karas spdk_bdev_queue_io_wait(job->bdev, job->ch, &task->bdev_io_wait); 89729784f35SKrzysztof Karas } 89829784f35SKrzysztof Karas 89929784f35SKrzysztof Karas static int 90029784f35SKrzysztof Karas bdevperf_job_drain(void *ctx) 90129784f35SKrzysztof Karas { 90229784f35SKrzysztof Karas struct bdevperf_job *job = ctx; 90329784f35SKrzysztof Karas 90429784f35SKrzysztof Karas spdk_poller_unregister(&job->run_timer); 90529784f35SKrzysztof Karas if (job->reset) { 90629784f35SKrzysztof Karas spdk_poller_unregister(&job->reset_timer); 90729784f35SKrzysztof Karas } 90829784f35SKrzysztof Karas 90929784f35SKrzysztof Karas job->is_draining = true; 91029784f35SKrzysztof Karas 91129784f35SKrzysztof Karas return -1; 91229784f35SKrzysztof Karas } 91329784f35SKrzysztof Karas 914893aaaccSAlexey Marchuk static int 915893aaaccSAlexey Marchuk bdevperf_job_drain_timer(void *ctx) 916893aaaccSAlexey Marchuk { 917893aaaccSAlexey Marchuk struct bdevperf_job *job = ctx; 918893aaaccSAlexey Marchuk 919893aaaccSAlexey Marchuk bdevperf_job_drain(ctx); 920893aaaccSAlexey Marchuk if (job->current_queue_depth == 0) { 921893aaaccSAlexey Marchuk bdevperf_job_empty(job); 922893aaaccSAlexey Marchuk } 923893aaaccSAlexey Marchuk 924893aaaccSAlexey Marchuk return SPDK_POLLER_BUSY; 925893aaaccSAlexey Marchuk } 926893aaaccSAlexey Marchuk 92729784f35SKrzysztof Karas static void 92829784f35SKrzysztof Karas bdevperf_abort_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 92929784f35SKrzysztof Karas { 93029784f35SKrzysztof Karas struct bdevperf_task *task = cb_arg; 93129784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 93229784f35SKrzysztof Karas 93329784f35SKrzysztof Karas job->current_queue_depth--; 93429784f35SKrzysztof Karas 93529784f35SKrzysztof Karas if (success) { 93629784f35SKrzysztof Karas job->io_completed++; 93729784f35SKrzysztof Karas } else { 93829784f35SKrzysztof Karas job->io_failed++; 93929784f35SKrzysztof Karas if (!job->continue_on_failure) { 94029784f35SKrzysztof Karas bdevperf_job_drain(job); 94129784f35SKrzysztof Karas g_run_rc = -1; 94229784f35SKrzysztof Karas } 94329784f35SKrzysztof Karas } 94429784f35SKrzysztof Karas 94529784f35SKrzysztof Karas spdk_bdev_free_io(bdev_io); 94629784f35SKrzysztof Karas bdevperf_end_task(task); 94729784f35SKrzysztof Karas } 94829784f35SKrzysztof Karas 94929784f35SKrzysztof Karas static int 950f15cea1dSShuhei Matsumoto bdevperf_verify_dif(struct bdevperf_task *task) 95129784f35SKrzysztof Karas { 95229784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 95329784f35SKrzysztof Karas struct spdk_bdev *bdev = job->bdev; 95429784f35SKrzysztof Karas struct spdk_dif_ctx dif_ctx; 95529784f35SKrzysztof Karas struct spdk_dif_error err_blk = {}; 95629784f35SKrzysztof Karas int rc; 957a711d629SSlawomir Ptak struct spdk_dif_ctx_init_ext_opts dif_opts; 95829784f35SKrzysztof Karas 9595681a8a6SKonrad Sztyber dif_opts.size = SPDK_SIZEOF(&dif_opts, dif_pi_format); 960dfaa72f9SShuhei Matsumoto dif_opts.dif_pi_format = spdk_bdev_get_dif_pi_format(bdev); 96129784f35SKrzysztof Karas rc = spdk_dif_ctx_init(&dif_ctx, 96229784f35SKrzysztof Karas spdk_bdev_get_block_size(bdev), 96329784f35SKrzysztof Karas spdk_bdev_get_md_size(bdev), 96429784f35SKrzysztof Karas spdk_bdev_is_md_interleaved(bdev), 96529784f35SKrzysztof Karas spdk_bdev_is_dif_head_of_md(bdev), 96629784f35SKrzysztof Karas spdk_bdev_get_dif_type(bdev), 96729784f35SKrzysztof Karas job->dif_check_flags, 968a711d629SSlawomir Ptak task->offset_blocks, 0, 0, 0, 0, &dif_opts); 96929784f35SKrzysztof Karas if (rc != 0) { 97029784f35SKrzysztof Karas fprintf(stderr, "Initialization of DIF context failed\n"); 97129784f35SKrzysztof Karas return rc; 97229784f35SKrzysztof Karas } 97329784f35SKrzysztof Karas 97429784f35SKrzysztof Karas if (spdk_bdev_is_md_interleaved(bdev)) { 975f15cea1dSShuhei Matsumoto rc = spdk_dif_verify(&task->iov, 1, job->io_size_blocks, &dif_ctx, &err_blk); 97629784f35SKrzysztof Karas } else { 97729784f35SKrzysztof Karas struct iovec md_iov = { 97829784f35SKrzysztof Karas .iov_base = task->md_buf, 97929784f35SKrzysztof Karas .iov_len = spdk_bdev_get_md_size(bdev) * job->io_size_blocks, 98029784f35SKrzysztof Karas }; 98129784f35SKrzysztof Karas 982f15cea1dSShuhei Matsumoto rc = spdk_dix_verify(&task->iov, 1, &md_iov, job->io_size_blocks, &dif_ctx, &err_blk); 98329784f35SKrzysztof Karas } 98429784f35SKrzysztof Karas 98529784f35SKrzysztof Karas if (rc != 0) { 98629784f35SKrzysztof Karas fprintf(stderr, "DIF/DIX error detected. type=%d, offset=%" PRIu32 "\n", 98729784f35SKrzysztof Karas err_blk.err_type, err_blk.err_offset); 98829784f35SKrzysztof Karas } 98929784f35SKrzysztof Karas 99029784f35SKrzysztof Karas return rc; 99129784f35SKrzysztof Karas } 99229784f35SKrzysztof Karas 99329784f35SKrzysztof Karas static void 99429784f35SKrzysztof Karas bdevperf_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 99529784f35SKrzysztof Karas { 99629784f35SKrzysztof Karas struct bdevperf_job *job; 99729784f35SKrzysztof Karas struct bdevperf_task *task = cb_arg; 99829784f35SKrzysztof Karas uint64_t offset_in_ios; 99929784f35SKrzysztof Karas int rc; 100029784f35SKrzysztof Karas 100129784f35SKrzysztof Karas job = task->job; 100229784f35SKrzysztof Karas 100329784f35SKrzysztof Karas if (g_error_to_exit == true) { 100429784f35SKrzysztof Karas bdevperf_job_drain(job); 100529784f35SKrzysztof Karas } else if (!success) { 100629784f35SKrzysztof Karas if (!job->reset && !job->continue_on_failure) { 100729784f35SKrzysztof Karas bdevperf_job_drain(job); 100829784f35SKrzysztof Karas g_run_rc = -1; 100929784f35SKrzysztof Karas g_error_to_exit = true; 101029784f35SKrzysztof Karas printf("task offset: %" PRIu64 " on job bdev=%s fails\n", 101129784f35SKrzysztof Karas task->offset_blocks, job->name); 101229784f35SKrzysztof Karas } 101329784f35SKrzysztof Karas } else if (job->verify || job->reset) { 1014f15cea1dSShuhei Matsumoto if (!verify_data(task->buf, job->buf_size, 1015f15cea1dSShuhei Matsumoto task->iov.iov_base, job->buf_size, 1016894d5af2SShuhei Matsumoto spdk_bdev_desc_get_block_size(job->bdev_desc), 101729784f35SKrzysztof Karas task->md_buf, spdk_bdev_io_get_md_buf(bdev_io), 1018894d5af2SShuhei Matsumoto spdk_bdev_desc_get_md_size(job->bdev_desc), 1019075fb5b8SShuhei Matsumoto job->io_size_blocks, job->md_check)) { 102029784f35SKrzysztof Karas printf("Buffer mismatch! Target: %s Disk Offset: %" PRIu64 "\n", job->name, task->offset_blocks); 102129784f35SKrzysztof Karas bdevperf_job_drain(job); 102229784f35SKrzysztof Karas g_run_rc = -1; 102329784f35SKrzysztof Karas } 102429784f35SKrzysztof Karas } else if (job->dif_check_flags != 0) { 1025894d5af2SShuhei Matsumoto if (task->io_type == SPDK_BDEV_IO_TYPE_READ && spdk_bdev_desc_get_md_size(job->bdev_desc) != 0) { 1026f15cea1dSShuhei Matsumoto rc = bdevperf_verify_dif(task); 102729784f35SKrzysztof Karas if (rc != 0) { 102829784f35SKrzysztof Karas printf("DIF error detected. task offset: %" PRIu64 " on job bdev=%s\n", 102929784f35SKrzysztof Karas task->offset_blocks, job->name); 103029784f35SKrzysztof Karas 103129784f35SKrzysztof Karas success = false; 103229784f35SKrzysztof Karas if (!job->reset && !job->continue_on_failure) { 103329784f35SKrzysztof Karas bdevperf_job_drain(job); 103429784f35SKrzysztof Karas g_run_rc = -1; 103529784f35SKrzysztof Karas g_error_to_exit = true; 103629784f35SKrzysztof Karas } 103729784f35SKrzysztof Karas } 103829784f35SKrzysztof Karas } 103929784f35SKrzysztof Karas } 104029784f35SKrzysztof Karas 104129784f35SKrzysztof Karas job->current_queue_depth--; 104229784f35SKrzysztof Karas 104329784f35SKrzysztof Karas if (success) { 104429784f35SKrzysztof Karas job->io_completed++; 104529784f35SKrzysztof Karas } else { 104629784f35SKrzysztof Karas job->io_failed++; 104729784f35SKrzysztof Karas } 104829784f35SKrzysztof Karas 104929784f35SKrzysztof Karas if (job->verify) { 105029784f35SKrzysztof Karas assert(task->offset_blocks / job->io_size_blocks >= job->ios_base); 105129784f35SKrzysztof Karas offset_in_ios = task->offset_blocks / job->io_size_blocks - job->ios_base; 105229784f35SKrzysztof Karas 105329784f35SKrzysztof Karas assert(spdk_bit_array_get(job->outstanding, offset_in_ios) == true); 105429784f35SKrzysztof Karas spdk_bit_array_clear(job->outstanding, offset_in_ios); 105529784f35SKrzysztof Karas } 105629784f35SKrzysztof Karas 105729784f35SKrzysztof Karas spdk_bdev_free_io(bdev_io); 105829784f35SKrzysztof Karas 105929784f35SKrzysztof Karas /* 106029784f35SKrzysztof Karas * is_draining indicates when time has expired for the test run 106129784f35SKrzysztof Karas * and we are just waiting for the previously submitted I/O 106229784f35SKrzysztof Karas * to complete. In this case, do not submit a new I/O to replace 106329784f35SKrzysztof Karas * the one just completed. 106429784f35SKrzysztof Karas */ 106529784f35SKrzysztof Karas if (!job->is_draining) { 106629784f35SKrzysztof Karas bdevperf_submit_single(job, task); 106729784f35SKrzysztof Karas } else { 106829784f35SKrzysztof Karas bdevperf_end_task(task); 106929784f35SKrzysztof Karas } 107029784f35SKrzysztof Karas } 107129784f35SKrzysztof Karas 107229784f35SKrzysztof Karas static void 107329784f35SKrzysztof Karas bdevperf_verify_submit_read(void *cb_arg) 107429784f35SKrzysztof Karas { 107529784f35SKrzysztof Karas struct bdevperf_job *job; 107629784f35SKrzysztof Karas struct bdevperf_task *task = cb_arg; 107729784f35SKrzysztof Karas int rc; 107829784f35SKrzysztof Karas 107929784f35SKrzysztof Karas job = task->job; 108029784f35SKrzysztof Karas 1081f15cea1dSShuhei Matsumoto task->iov.iov_base = task->verify_buf; 1082f15cea1dSShuhei Matsumoto task->iov.iov_len = job->buf_size; 1083f15cea1dSShuhei Matsumoto 108429784f35SKrzysztof Karas /* Read the data back in */ 1085dc3ea9d2SSlawomir Ptak rc = spdk_bdev_readv_blocks_with_md(job->bdev_desc, job->ch, &task->iov, 1, task->verify_md_buf, 108629784f35SKrzysztof Karas task->offset_blocks, job->io_size_blocks, 1087ed08fd27STomasz Zawadzki bdevperf_complete, task); 108829784f35SKrzysztof Karas 108929784f35SKrzysztof Karas if (rc == -ENOMEM) { 109029784f35SKrzysztof Karas bdevperf_queue_io_wait_with_cb(task, bdevperf_verify_submit_read); 109129784f35SKrzysztof Karas } else if (rc != 0) { 109229784f35SKrzysztof Karas printf("Failed to submit read: %d\n", rc); 109329784f35SKrzysztof Karas bdevperf_job_drain(job); 109429784f35SKrzysztof Karas g_run_rc = rc; 109529784f35SKrzysztof Karas } 109629784f35SKrzysztof Karas } 109729784f35SKrzysztof Karas 109829784f35SKrzysztof Karas static void 109929784f35SKrzysztof Karas bdevperf_verify_write_complete(struct spdk_bdev_io *bdev_io, bool success, 110029784f35SKrzysztof Karas void *cb_arg) 110129784f35SKrzysztof Karas { 110229784f35SKrzysztof Karas if (success) { 110329784f35SKrzysztof Karas spdk_bdev_free_io(bdev_io); 110429784f35SKrzysztof Karas bdevperf_verify_submit_read(cb_arg); 110529784f35SKrzysztof Karas } else { 110629784f35SKrzysztof Karas bdevperf_complete(bdev_io, success, cb_arg); 110729784f35SKrzysztof Karas } 110829784f35SKrzysztof Karas } 110929784f35SKrzysztof Karas 111029784f35SKrzysztof Karas static void 111129784f35SKrzysztof Karas bdevperf_zcopy_populate_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 111229784f35SKrzysztof Karas { 111329784f35SKrzysztof Karas if (!success) { 111429784f35SKrzysztof Karas bdevperf_complete(bdev_io, success, cb_arg); 111529784f35SKrzysztof Karas return; 111629784f35SKrzysztof Karas } 111729784f35SKrzysztof Karas 111829784f35SKrzysztof Karas spdk_bdev_zcopy_end(bdev_io, false, bdevperf_complete, cb_arg); 111929784f35SKrzysztof Karas } 112029784f35SKrzysztof Karas 112129784f35SKrzysztof Karas static int 112229784f35SKrzysztof Karas bdevperf_generate_dif(struct bdevperf_task *task) 112329784f35SKrzysztof Karas { 112429784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 1125894d5af2SShuhei Matsumoto struct spdk_bdev_desc *desc = job->bdev_desc; 112629784f35SKrzysztof Karas struct spdk_dif_ctx dif_ctx; 112729784f35SKrzysztof Karas int rc; 1128a711d629SSlawomir Ptak struct spdk_dif_ctx_init_ext_opts dif_opts; 112929784f35SKrzysztof Karas 11305681a8a6SKonrad Sztyber dif_opts.size = SPDK_SIZEOF(&dif_opts, dif_pi_format); 1131894d5af2SShuhei Matsumoto dif_opts.dif_pi_format = spdk_bdev_desc_get_dif_pi_format(desc); 113229784f35SKrzysztof Karas rc = spdk_dif_ctx_init(&dif_ctx, 1133894d5af2SShuhei Matsumoto spdk_bdev_desc_get_block_size(desc), 1134894d5af2SShuhei Matsumoto spdk_bdev_desc_get_md_size(desc), 1135894d5af2SShuhei Matsumoto spdk_bdev_desc_is_md_interleaved(desc), 1136894d5af2SShuhei Matsumoto spdk_bdev_desc_is_dif_head_of_md(desc), 1137894d5af2SShuhei Matsumoto spdk_bdev_desc_get_dif_type(desc), 113829784f35SKrzysztof Karas job->dif_check_flags, 1139a711d629SSlawomir Ptak task->offset_blocks, 0, 0, 0, 0, &dif_opts); 114029784f35SKrzysztof Karas if (rc != 0) { 114129784f35SKrzysztof Karas fprintf(stderr, "Initialization of DIF context failed\n"); 114229784f35SKrzysztof Karas return rc; 114329784f35SKrzysztof Karas } 114429784f35SKrzysztof Karas 1145894d5af2SShuhei Matsumoto if (spdk_bdev_desc_is_md_interleaved(desc)) { 114629784f35SKrzysztof Karas rc = spdk_dif_generate(&task->iov, 1, job->io_size_blocks, &dif_ctx); 114729784f35SKrzysztof Karas } else { 114829784f35SKrzysztof Karas struct iovec md_iov = { 114929784f35SKrzysztof Karas .iov_base = task->md_buf, 1150894d5af2SShuhei Matsumoto .iov_len = spdk_bdev_desc_get_md_size(desc) * job->io_size_blocks, 115129784f35SKrzysztof Karas }; 115229784f35SKrzysztof Karas 115329784f35SKrzysztof Karas rc = spdk_dix_generate(&task->iov, 1, &md_iov, job->io_size_blocks, &dif_ctx); 115429784f35SKrzysztof Karas } 115529784f35SKrzysztof Karas 115629784f35SKrzysztof Karas if (rc != 0) { 115729784f35SKrzysztof Karas fprintf(stderr, "Generation of DIF/DIX failed\n"); 115829784f35SKrzysztof Karas } 115929784f35SKrzysztof Karas 116029784f35SKrzysztof Karas return rc; 116129784f35SKrzysztof Karas } 116229784f35SKrzysztof Karas 116329784f35SKrzysztof Karas static void 116429784f35SKrzysztof Karas bdevperf_submit_task(void *arg) 116529784f35SKrzysztof Karas { 116629784f35SKrzysztof Karas struct bdevperf_task *task = arg; 116729784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 116829784f35SKrzysztof Karas struct spdk_bdev_desc *desc; 116929784f35SKrzysztof Karas struct spdk_io_channel *ch; 117029784f35SKrzysztof Karas spdk_bdev_io_completion_cb cb_fn; 117129784f35SKrzysztof Karas uint64_t offset_in_ios; 117229784f35SKrzysztof Karas int rc = 0; 117329784f35SKrzysztof Karas 117429784f35SKrzysztof Karas desc = job->bdev_desc; 117529784f35SKrzysztof Karas ch = job->ch; 117629784f35SKrzysztof Karas 117729784f35SKrzysztof Karas switch (task->io_type) { 117829784f35SKrzysztof Karas case SPDK_BDEV_IO_TYPE_WRITE: 1179894d5af2SShuhei Matsumoto if (spdk_bdev_desc_get_md_size(desc) != 0 && job->dif_check_flags != 0) { 118029784f35SKrzysztof Karas rc = bdevperf_generate_dif(task); 118129784f35SKrzysztof Karas } 118229784f35SKrzysztof Karas if (rc == 0) { 118329784f35SKrzysztof Karas cb_fn = (job->verify || job->reset) ? bdevperf_verify_write_complete : bdevperf_complete; 118429784f35SKrzysztof Karas 118529784f35SKrzysztof Karas if (g_zcopy) { 118629784f35SKrzysztof Karas spdk_bdev_zcopy_end(task->bdev_io, true, cb_fn, task); 118729784f35SKrzysztof Karas return; 118829784f35SKrzysztof Karas } else { 1189ed08fd27STomasz Zawadzki rc = spdk_bdev_writev_blocks_with_md(desc, ch, &task->iov, 1, 1190ed08fd27STomasz Zawadzki task->md_buf, 119129784f35SKrzysztof Karas task->offset_blocks, 119229784f35SKrzysztof Karas job->io_size_blocks, 1193ed08fd27STomasz Zawadzki cb_fn, task); 119429784f35SKrzysztof Karas } 119529784f35SKrzysztof Karas } 119629784f35SKrzysztof Karas break; 119729784f35SKrzysztof Karas case SPDK_BDEV_IO_TYPE_FLUSH: 119829784f35SKrzysztof Karas rc = spdk_bdev_flush_blocks(desc, ch, task->offset_blocks, 119929784f35SKrzysztof Karas job->io_size_blocks, bdevperf_complete, task); 120029784f35SKrzysztof Karas break; 120129784f35SKrzysztof Karas case SPDK_BDEV_IO_TYPE_UNMAP: 120229784f35SKrzysztof Karas rc = spdk_bdev_unmap_blocks(desc, ch, task->offset_blocks, 120329784f35SKrzysztof Karas job->io_size_blocks, bdevperf_complete, task); 120429784f35SKrzysztof Karas break; 120529784f35SKrzysztof Karas case SPDK_BDEV_IO_TYPE_WRITE_ZEROES: 120629784f35SKrzysztof Karas rc = spdk_bdev_write_zeroes_blocks(desc, ch, task->offset_blocks, 120729784f35SKrzysztof Karas job->io_size_blocks, bdevperf_complete, task); 120829784f35SKrzysztof Karas break; 120929784f35SKrzysztof Karas case SPDK_BDEV_IO_TYPE_READ: 121029784f35SKrzysztof Karas if (g_zcopy) { 121129784f35SKrzysztof Karas rc = spdk_bdev_zcopy_start(desc, ch, NULL, 0, task->offset_blocks, job->io_size_blocks, 121229784f35SKrzysztof Karas true, bdevperf_zcopy_populate_complete, task); 121329784f35SKrzysztof Karas } else { 1214ed08fd27STomasz Zawadzki rc = spdk_bdev_readv_blocks_with_md(desc, ch, &task->iov, 1, 1215ed08fd27STomasz Zawadzki task->md_buf, 121629784f35SKrzysztof Karas task->offset_blocks, 121729784f35SKrzysztof Karas job->io_size_blocks, 1218ed08fd27STomasz Zawadzki bdevperf_complete, task); 121929784f35SKrzysztof Karas } 122029784f35SKrzysztof Karas break; 122129784f35SKrzysztof Karas case SPDK_BDEV_IO_TYPE_ABORT: 122229784f35SKrzysztof Karas rc = spdk_bdev_abort(desc, ch, task->task_to_abort, bdevperf_abort_complete, task); 122329784f35SKrzysztof Karas break; 122429784f35SKrzysztof Karas default: 122529784f35SKrzysztof Karas assert(false); 122629784f35SKrzysztof Karas rc = -EINVAL; 122729784f35SKrzysztof Karas break; 122829784f35SKrzysztof Karas } 122929784f35SKrzysztof Karas 123029784f35SKrzysztof Karas if (rc == -ENOMEM) { 123129784f35SKrzysztof Karas bdevperf_queue_io_wait_with_cb(task, bdevperf_submit_task); 123229784f35SKrzysztof Karas return; 123329784f35SKrzysztof Karas } else if (rc != 0) { 123429784f35SKrzysztof Karas printf("Failed to submit bdev_io: %d\n", rc); 123529784f35SKrzysztof Karas if (job->verify) { 123629784f35SKrzysztof Karas assert(task->offset_blocks / job->io_size_blocks >= job->ios_base); 123729784f35SKrzysztof Karas offset_in_ios = task->offset_blocks / job->io_size_blocks - job->ios_base; 123829784f35SKrzysztof Karas 123929784f35SKrzysztof Karas assert(spdk_bit_array_get(job->outstanding, offset_in_ios) == true); 124029784f35SKrzysztof Karas spdk_bit_array_clear(job->outstanding, offset_in_ios); 124129784f35SKrzysztof Karas } 124229784f35SKrzysztof Karas bdevperf_job_drain(job); 124329784f35SKrzysztof Karas g_run_rc = rc; 124429784f35SKrzysztof Karas return; 124529784f35SKrzysztof Karas } 124629784f35SKrzysztof Karas 124729784f35SKrzysztof Karas job->current_queue_depth++; 124829784f35SKrzysztof Karas } 124929784f35SKrzysztof Karas 125029784f35SKrzysztof Karas static void 125129784f35SKrzysztof Karas bdevperf_zcopy_get_buf_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 125229784f35SKrzysztof Karas { 125329784f35SKrzysztof Karas struct bdevperf_task *task = cb_arg; 125429784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 125529784f35SKrzysztof Karas struct iovec *iovs; 125629784f35SKrzysztof Karas int iovcnt; 125729784f35SKrzysztof Karas 125829784f35SKrzysztof Karas if (!success) { 125929784f35SKrzysztof Karas bdevperf_job_drain(job); 126029784f35SKrzysztof Karas g_run_rc = -1; 126129784f35SKrzysztof Karas return; 126229784f35SKrzysztof Karas } 126329784f35SKrzysztof Karas 126429784f35SKrzysztof Karas task->bdev_io = bdev_io; 126529784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_WRITE; 126629784f35SKrzysztof Karas 126729784f35SKrzysztof Karas if (job->verify || job->reset) { 126829784f35SKrzysztof Karas /* When job->verify or job->reset is enabled, task->buf is used for 126929784f35SKrzysztof Karas * verification of read after write. For write I/O, when zcopy APIs 127029784f35SKrzysztof Karas * are used, task->buf cannot be used, and data must be written to 127129784f35SKrzysztof Karas * the data buffer allocated underneath bdev layer instead. 127229784f35SKrzysztof Karas * Hence we copy task->buf to the allocated data buffer here. 127329784f35SKrzysztof Karas */ 127429784f35SKrzysztof Karas spdk_bdev_io_get_iovec(bdev_io, &iovs, &iovcnt); 127529784f35SKrzysztof Karas assert(iovcnt == 1); 127629784f35SKrzysztof Karas assert(iovs != NULL); 127729784f35SKrzysztof Karas 127829784f35SKrzysztof Karas copy_data(iovs[0].iov_base, iovs[0].iov_len, task->buf, job->buf_size, 1279894d5af2SShuhei Matsumoto spdk_bdev_desc_get_block_size(job->bdev_desc), 128029784f35SKrzysztof Karas spdk_bdev_io_get_md_buf(bdev_io), task->md_buf, 1281894d5af2SShuhei Matsumoto spdk_bdev_desc_get_md_size(job->bdev_desc), job->io_size_blocks); 128229784f35SKrzysztof Karas } 128329784f35SKrzysztof Karas 128429784f35SKrzysztof Karas bdevperf_submit_task(task); 128529784f35SKrzysztof Karas } 128629784f35SKrzysztof Karas 128729784f35SKrzysztof Karas static void 128829784f35SKrzysztof Karas bdevperf_prep_zcopy_write_task(void *arg) 128929784f35SKrzysztof Karas { 129029784f35SKrzysztof Karas struct bdevperf_task *task = arg; 129129784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 129229784f35SKrzysztof Karas int rc; 129329784f35SKrzysztof Karas 129429784f35SKrzysztof Karas rc = spdk_bdev_zcopy_start(job->bdev_desc, job->ch, NULL, 0, 129529784f35SKrzysztof Karas task->offset_blocks, job->io_size_blocks, 129629784f35SKrzysztof Karas false, bdevperf_zcopy_get_buf_complete, task); 129729784f35SKrzysztof Karas if (rc != 0) { 129829784f35SKrzysztof Karas assert(rc == -ENOMEM); 129929784f35SKrzysztof Karas bdevperf_queue_io_wait_with_cb(task, bdevperf_prep_zcopy_write_task); 130029784f35SKrzysztof Karas return; 130129784f35SKrzysztof Karas } 130229784f35SKrzysztof Karas 130329784f35SKrzysztof Karas job->current_queue_depth++; 130429784f35SKrzysztof Karas } 130529784f35SKrzysztof Karas 130629784f35SKrzysztof Karas static struct bdevperf_task * 130729784f35SKrzysztof Karas bdevperf_job_get_task(struct bdevperf_job *job) 130829784f35SKrzysztof Karas { 130929784f35SKrzysztof Karas struct bdevperf_task *task; 131029784f35SKrzysztof Karas 131129784f35SKrzysztof Karas task = TAILQ_FIRST(&job->task_list); 131229784f35SKrzysztof Karas if (!task) { 131329784f35SKrzysztof Karas printf("Task allocation failed\n"); 131429784f35SKrzysztof Karas abort(); 131529784f35SKrzysztof Karas } 131629784f35SKrzysztof Karas 131729784f35SKrzysztof Karas TAILQ_REMOVE(&job->task_list, task, link); 131829784f35SKrzysztof Karas return task; 131929784f35SKrzysztof Karas } 132029784f35SKrzysztof Karas 132129784f35SKrzysztof Karas static void 132229784f35SKrzysztof Karas bdevperf_submit_single(struct bdevperf_job *job, struct bdevperf_task *task) 132329784f35SKrzysztof Karas { 132429784f35SKrzysztof Karas uint64_t offset_in_ios; 13250d11cf93SJim Harris uint64_t rand_value; 1326d254a3b9SSlawomir Ptak uint32_t first_clear; 132729784f35SKrzysztof Karas 132829784f35SKrzysztof Karas if (job->zipf) { 132929784f35SKrzysztof Karas offset_in_ios = spdk_zipf_generate(job->zipf); 133029784f35SKrzysztof Karas } else if (job->is_random) { 13310d11cf93SJim Harris /* RAND_MAX is only INT32_MAX, so use 2 calls to rand_r to 13320d11cf93SJim Harris * get a large enough value to ensure we are issuing I/O 13330d11cf93SJim Harris * uniformly across the whole bdev. 13340d11cf93SJim Harris */ 13350d11cf93SJim Harris rand_value = (uint64_t)rand_r(&job->seed) * RAND_MAX + rand_r(&job->seed); 13360d11cf93SJim Harris offset_in_ios = rand_value % job->size_in_ios; 1337d254a3b9SSlawomir Ptak 1338d254a3b9SSlawomir Ptak if (g_random_map) { 1339d254a3b9SSlawomir Ptak /* Make sure, that the offset does not exceed the maximum size 1340d254a3b9SSlawomir Ptak * of the bit array (verified during job creation) 1341d254a3b9SSlawomir Ptak */ 1342d254a3b9SSlawomir Ptak assert(offset_in_ios < UINT32_MAX); 1343d254a3b9SSlawomir Ptak 1344d254a3b9SSlawomir Ptak first_clear = spdk_bit_array_find_first_clear(job->random_map, (uint32_t)offset_in_ios); 1345d254a3b9SSlawomir Ptak 1346d254a3b9SSlawomir Ptak if (first_clear == UINT32_MAX) { 1347d254a3b9SSlawomir Ptak first_clear = spdk_bit_array_find_first_clear(job->random_map, 0); 1348d254a3b9SSlawomir Ptak 1349d254a3b9SSlawomir Ptak if (first_clear == UINT32_MAX) { 1350d254a3b9SSlawomir Ptak /* If there are no more clear bits in the array, we start over 1351d254a3b9SSlawomir Ptak * and select the previously selected random value. 1352d254a3b9SSlawomir Ptak */ 1353d254a3b9SSlawomir Ptak spdk_bit_array_clear_mask(job->random_map); 1354d254a3b9SSlawomir Ptak first_clear = (uint32_t)offset_in_ios; 1355d254a3b9SSlawomir Ptak } 1356d254a3b9SSlawomir Ptak } 1357d254a3b9SSlawomir Ptak 1358d254a3b9SSlawomir Ptak spdk_bit_array_set(job->random_map, first_clear); 1359d254a3b9SSlawomir Ptak 1360d254a3b9SSlawomir Ptak offset_in_ios = first_clear; 1361d254a3b9SSlawomir Ptak } 136229784f35SKrzysztof Karas } else { 136329784f35SKrzysztof Karas offset_in_ios = job->offset_in_ios++; 136429784f35SKrzysztof Karas if (job->offset_in_ios == job->size_in_ios) { 136529784f35SKrzysztof Karas job->offset_in_ios = 0; 136629784f35SKrzysztof Karas } 136729784f35SKrzysztof Karas 136829784f35SKrzysztof Karas /* Increment of offset_in_ios if there's already an outstanding IO 136929784f35SKrzysztof Karas * to that location. We only need this with job->verify as random 137029784f35SKrzysztof Karas * offsets are not supported with job->verify at this time. 137129784f35SKrzysztof Karas */ 137229784f35SKrzysztof Karas if (job->verify) { 137329784f35SKrzysztof Karas assert(spdk_bit_array_find_first_clear(job->outstanding, 0) != UINT32_MAX); 137429784f35SKrzysztof Karas 137529784f35SKrzysztof Karas while (spdk_bit_array_get(job->outstanding, offset_in_ios)) { 137629784f35SKrzysztof Karas offset_in_ios = job->offset_in_ios++; 137729784f35SKrzysztof Karas if (job->offset_in_ios == job->size_in_ios) { 137829784f35SKrzysztof Karas job->offset_in_ios = 0; 137929784f35SKrzysztof Karas } 138029784f35SKrzysztof Karas } 138129784f35SKrzysztof Karas spdk_bit_array_set(job->outstanding, offset_in_ios); 138229784f35SKrzysztof Karas } 138329784f35SKrzysztof Karas } 138429784f35SKrzysztof Karas 138529784f35SKrzysztof Karas /* For multi-thread to same job, offset_in_ios is relative 138629784f35SKrzysztof Karas * to the LBA range assigned for that job. job->offset_blocks 138729784f35SKrzysztof Karas * is absolute (entire bdev LBA range). 138829784f35SKrzysztof Karas */ 138929784f35SKrzysztof Karas task->offset_blocks = (offset_in_ios + job->ios_base) * job->io_size_blocks; 139029784f35SKrzysztof Karas 13913353236fSArtur Paszkiewicz if (job->flush) { 139229784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_FLUSH; 139329784f35SKrzysztof Karas } else if (job->unmap) { 139429784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_UNMAP; 139529784f35SKrzysztof Karas } else if (job->write_zeroes) { 139629784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_WRITE_ZEROES; 139729784f35SKrzysztof Karas } else if ((job->rw_percentage == 100) || 139829784f35SKrzysztof Karas (job->rw_percentage != 0 && ((rand_r(&job->seed) % 100) < job->rw_percentage))) { 1399b2cfb6b4SJacek Kalwas assert(!job->verify); 140029784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_READ; 1401f15cea1dSShuhei Matsumoto if (!g_zcopy) { 1402f15cea1dSShuhei Matsumoto task->iov.iov_base = task->buf; 1403f15cea1dSShuhei Matsumoto task->iov.iov_len = job->buf_size; 1404f15cea1dSShuhei Matsumoto } 140529784f35SKrzysztof Karas } else { 14063353236fSArtur Paszkiewicz if (job->verify || job->reset || g_unique_writes) { 14073353236fSArtur Paszkiewicz generate_data(job, task->buf, task->md_buf, g_unique_writes); 14083353236fSArtur Paszkiewicz } 140929784f35SKrzysztof Karas if (g_zcopy) { 141029784f35SKrzysztof Karas bdevperf_prep_zcopy_write_task(task); 141129784f35SKrzysztof Karas return; 141229784f35SKrzysztof Karas } else { 141329784f35SKrzysztof Karas task->iov.iov_base = task->buf; 141429784f35SKrzysztof Karas task->iov.iov_len = job->buf_size; 141529784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_WRITE; 141629784f35SKrzysztof Karas } 141729784f35SKrzysztof Karas } 141829784f35SKrzysztof Karas 141929784f35SKrzysztof Karas bdevperf_submit_task(task); 142029784f35SKrzysztof Karas } 142129784f35SKrzysztof Karas 142229784f35SKrzysztof Karas static int reset_job(void *arg); 142329784f35SKrzysztof Karas 142429784f35SKrzysztof Karas static void 142529784f35SKrzysztof Karas reset_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 142629784f35SKrzysztof Karas { 142729784f35SKrzysztof Karas struct bdevperf_task *task = cb_arg; 142829784f35SKrzysztof Karas struct bdevperf_job *job = task->job; 142929784f35SKrzysztof Karas 143029784f35SKrzysztof Karas if (!success) { 143129784f35SKrzysztof Karas printf("Reset blockdev=%s failed\n", spdk_bdev_get_name(job->bdev)); 143229784f35SKrzysztof Karas bdevperf_job_drain(job); 143329784f35SKrzysztof Karas g_run_rc = -1; 143429784f35SKrzysztof Karas } 143529784f35SKrzysztof Karas 143629784f35SKrzysztof Karas TAILQ_INSERT_TAIL(&job->task_list, task, link); 143729784f35SKrzysztof Karas spdk_bdev_free_io(bdev_io); 143829784f35SKrzysztof Karas 143929784f35SKrzysztof Karas job->reset_timer = SPDK_POLLER_REGISTER(reset_job, job, 1440045c781dSRichael Zhuang 10 * SPDK_SEC_TO_USEC); 144129784f35SKrzysztof Karas } 144229784f35SKrzysztof Karas 144329784f35SKrzysztof Karas static int 144429784f35SKrzysztof Karas reset_job(void *arg) 144529784f35SKrzysztof Karas { 144629784f35SKrzysztof Karas struct bdevperf_job *job = arg; 144729784f35SKrzysztof Karas struct bdevperf_task *task; 144829784f35SKrzysztof Karas int rc; 144929784f35SKrzysztof Karas 145029784f35SKrzysztof Karas spdk_poller_unregister(&job->reset_timer); 145129784f35SKrzysztof Karas 145229784f35SKrzysztof Karas /* Do reset. */ 145329784f35SKrzysztof Karas task = bdevperf_job_get_task(job); 145429784f35SKrzysztof Karas rc = spdk_bdev_reset(job->bdev_desc, job->ch, 145529784f35SKrzysztof Karas reset_cb, task); 145629784f35SKrzysztof Karas if (rc) { 145729784f35SKrzysztof Karas printf("Reset failed: %d\n", rc); 145829784f35SKrzysztof Karas bdevperf_job_drain(job); 145929784f35SKrzysztof Karas g_run_rc = -1; 146029784f35SKrzysztof Karas } 146129784f35SKrzysztof Karas 146229784f35SKrzysztof Karas return -1; 146329784f35SKrzysztof Karas } 146429784f35SKrzysztof Karas 146529784f35SKrzysztof Karas static void 146629784f35SKrzysztof Karas bdevperf_timeout_cb(void *cb_arg, struct spdk_bdev_io *bdev_io) 146729784f35SKrzysztof Karas { 146829784f35SKrzysztof Karas struct bdevperf_job *job = cb_arg; 146929784f35SKrzysztof Karas struct bdevperf_task *task; 147029784f35SKrzysztof Karas 147129784f35SKrzysztof Karas job->io_timeout++; 147229784f35SKrzysztof Karas 147329784f35SKrzysztof Karas if (job->is_draining || !job->abort || 147429784f35SKrzysztof Karas !spdk_bdev_io_type_supported(job->bdev, SPDK_BDEV_IO_TYPE_ABORT)) { 147529784f35SKrzysztof Karas return; 147629784f35SKrzysztof Karas } 147729784f35SKrzysztof Karas 147829784f35SKrzysztof Karas task = bdevperf_job_get_task(job); 147929784f35SKrzysztof Karas if (task == NULL) { 148029784f35SKrzysztof Karas return; 148129784f35SKrzysztof Karas } 148229784f35SKrzysztof Karas 148329784f35SKrzysztof Karas task->task_to_abort = spdk_bdev_io_get_cb_arg(bdev_io); 148429784f35SKrzysztof Karas task->io_type = SPDK_BDEV_IO_TYPE_ABORT; 148529784f35SKrzysztof Karas 148629784f35SKrzysztof Karas bdevperf_submit_task(task); 148729784f35SKrzysztof Karas } 148829784f35SKrzysztof Karas 148929784f35SKrzysztof Karas static void 149029784f35SKrzysztof Karas bdevperf_job_run(void *ctx) 149129784f35SKrzysztof Karas { 149229784f35SKrzysztof Karas struct bdevperf_job *job = ctx; 149329784f35SKrzysztof Karas struct bdevperf_task *task; 149429784f35SKrzysztof Karas int i; 149529784f35SKrzysztof Karas 149629784f35SKrzysztof Karas /* Submit initial I/O for this job. Each time one 149729784f35SKrzysztof Karas * completes, another will be submitted. */ 149829784f35SKrzysztof Karas 149929784f35SKrzysztof Karas /* Start a timer to stop this I/O chain when the run is over */ 1500893aaaccSAlexey Marchuk job->run_timer = SPDK_POLLER_REGISTER(bdevperf_job_drain_timer, job, g_time_in_usec); 150129784f35SKrzysztof Karas if (job->reset) { 150229784f35SKrzysztof Karas job->reset_timer = SPDK_POLLER_REGISTER(reset_job, job, 1503045c781dSRichael Zhuang 10 * SPDK_SEC_TO_USEC); 150429784f35SKrzysztof Karas } 150529784f35SKrzysztof Karas 150629784f35SKrzysztof Karas for (i = 0; i < job->queue_depth; i++) { 150729784f35SKrzysztof Karas task = bdevperf_job_get_task(job); 150829784f35SKrzysztof Karas bdevperf_submit_single(job, task); 150929784f35SKrzysztof Karas } 151029784f35SKrzysztof Karas } 151129784f35SKrzysztof Karas 151229784f35SKrzysztof Karas static void 151329784f35SKrzysztof Karas _performance_dump_done(void *ctx) 151429784f35SKrzysztof Karas { 1515c273168dSMarcin Spiewak struct bdevperf_aggregate_stats *aggregate = ctx; 1516c273168dSMarcin Spiewak struct bdevperf_stats *stats = &aggregate->total; 1517723dd06eSRichael Zhuang double average_latency; 151829784f35SKrzysztof Karas 151927134348SAnisa Su if (g_summarize_performance) { 152027134348SAnisa Su printf("%12.2f IOPS, %8.2f MiB/s", stats->total_io_per_second, stats->total_mb_per_second); 152127134348SAnisa Su printf("\r"); 152227134348SAnisa Su } else { 1523723dd06eSRichael Zhuang printf("\r ==================================================================================" 1524723dd06eSRichael Zhuang "=================================\n"); 152529784f35SKrzysztof Karas printf("\r %-28s: %10s %10.2f %10.2f", 152629784f35SKrzysztof Karas "Total", "", stats->total_io_per_second, stats->total_mb_per_second); 1527723dd06eSRichael Zhuang printf(" %10.2f %8.2f", 152829784f35SKrzysztof Karas stats->total_failed_per_second, stats->total_timeout_per_second); 1529723dd06eSRichael Zhuang 1530045c781dSRichael Zhuang average_latency = ((double)stats->total_tsc / stats->total_io_completed) * SPDK_SEC_TO_USEC / 1531723dd06eSRichael Zhuang spdk_get_ticks_hz(); 1532723dd06eSRichael Zhuang printf(" %10.2f %10.2f %10.2f\n", average_latency, stats->min_latency, stats->max_latency); 1533723dd06eSRichael Zhuang printf("\n"); 153427134348SAnisa Su } 1535723dd06eSRichael Zhuang 153629784f35SKrzysztof Karas fflush(stdout); 153729784f35SKrzysztof Karas 153829784f35SKrzysztof Karas g_performance_dump_active = false; 153929784f35SKrzysztof Karas 1540c273168dSMarcin Spiewak free(aggregate); 154129784f35SKrzysztof Karas } 154229784f35SKrzysztof Karas 154329784f35SKrzysztof Karas static void 154429784f35SKrzysztof Karas _performance_dump(void *ctx) 154529784f35SKrzysztof Karas { 154629784f35SKrzysztof Karas struct bdevperf_aggregate_stats *stats = ctx; 154772177d39SMarcin Spiewak struct bdevperf_stats job_stats = {0}; 154872177d39SMarcin Spiewak struct bdevperf_job *job = stats->current_job; 154972177d39SMarcin Spiewak uint64_t time_in_usec; 155029784f35SKrzysztof Karas 155172177d39SMarcin Spiewak if (job->io_failed > 0 && !job->continue_on_failure) { 155272177d39SMarcin Spiewak time_in_usec = job->run_time_in_usec; 155372177d39SMarcin Spiewak } else { 155472177d39SMarcin Spiewak time_in_usec = stats->total.io_time_in_usec; 155572177d39SMarcin Spiewak } 155672177d39SMarcin Spiewak 155772177d39SMarcin Spiewak bdevperf_job_get_stats(job, &job_stats, time_in_usec, g_show_performance_ema_period); 155872177d39SMarcin Spiewak bdevperf_job_stats_accumulate(&stats->total, &job_stats); 155972177d39SMarcin Spiewak if (!g_summarize_performance) { 156072177d39SMarcin Spiewak performance_dump_job_stdout(stats->current_job, &job_stats); 156172177d39SMarcin Spiewak } 156229784f35SKrzysztof Karas 156329784f35SKrzysztof Karas /* This assumes the jobs list is static after start up time. 156429784f35SKrzysztof Karas * That's true right now, but if that ever changed this would need a lock. */ 156529784f35SKrzysztof Karas stats->current_job = TAILQ_NEXT(stats->current_job, link); 156629784f35SKrzysztof Karas if (stats->current_job == NULL) { 156729784f35SKrzysztof Karas spdk_thread_send_msg(g_main_thread, _performance_dump_done, stats); 156829784f35SKrzysztof Karas } else { 156929784f35SKrzysztof Karas spdk_thread_send_msg(stats->current_job->thread, _performance_dump, stats); 157029784f35SKrzysztof Karas } 157129784f35SKrzysztof Karas } 157229784f35SKrzysztof Karas 157329784f35SKrzysztof Karas static int 157429784f35SKrzysztof Karas performance_statistics_thread(void *arg) 157529784f35SKrzysztof Karas { 1576c273168dSMarcin Spiewak struct bdevperf_aggregate_stats *aggregate; 1577c273168dSMarcin Spiewak struct bdevperf_stats *stats; 1578c273168dSMarcin Spiewak 157929784f35SKrzysztof Karas 158029784f35SKrzysztof Karas if (g_performance_dump_active) { 158129784f35SKrzysztof Karas return -1; 158229784f35SKrzysztof Karas } 158329784f35SKrzysztof Karas 158429784f35SKrzysztof Karas g_performance_dump_active = true; 158529784f35SKrzysztof Karas 1586c273168dSMarcin Spiewak aggregate = calloc(1, sizeof(*aggregate)); 1587c273168dSMarcin Spiewak if (aggregate == NULL) { 158829784f35SKrzysztof Karas return -1; 158929784f35SKrzysztof Karas } 1590c273168dSMarcin Spiewak stats = &aggregate->total; 1591723dd06eSRichael Zhuang stats->min_latency = (double)UINT64_MAX; 1592723dd06eSRichael Zhuang 159329784f35SKrzysztof Karas g_show_performance_period_num++; 159429784f35SKrzysztof Karas 159529784f35SKrzysztof Karas stats->io_time_in_usec = g_show_performance_period_num * g_show_performance_period_in_usec; 159629784f35SKrzysztof Karas 159729784f35SKrzysztof Karas /* Iterate all of the jobs to gather stats 159829784f35SKrzysztof Karas * These jobs will not get removed here until a final performance dump is run, 159929784f35SKrzysztof Karas * so this should be safe without locking. 160029784f35SKrzysztof Karas */ 1601c273168dSMarcin Spiewak aggregate->current_job = TAILQ_FIRST(&g_bdevperf.jobs); 1602c273168dSMarcin Spiewak if (aggregate->current_job == NULL) { 1603c273168dSMarcin Spiewak spdk_thread_send_msg(g_main_thread, _performance_dump_done, aggregate); 160429784f35SKrzysztof Karas } else { 1605c273168dSMarcin Spiewak spdk_thread_send_msg(aggregate->current_job->thread, _performance_dump, aggregate); 160629784f35SKrzysztof Karas } 160729784f35SKrzysztof Karas 160829784f35SKrzysztof Karas return -1; 160929784f35SKrzysztof Karas } 161029784f35SKrzysztof Karas 161129784f35SKrzysztof Karas static void 161229784f35SKrzysztof Karas bdevperf_test(void) 161329784f35SKrzysztof Karas { 161429784f35SKrzysztof Karas struct bdevperf_job *job; 161529784f35SKrzysztof Karas 161665fa5affSMarcin Spiewak if (TAILQ_EMPTY(&g_bdevperf.jobs)) { 161765fa5affSMarcin Spiewak if (g_request) { 161865fa5affSMarcin Spiewak spdk_jsonrpc_send_error_response_fmt(g_request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, 161965fa5affSMarcin Spiewak "No jobs defined or bdevs created"); 162065fa5affSMarcin Spiewak g_request = NULL; 162165fa5affSMarcin Spiewak } 162265fa5affSMarcin Spiewak return; 162365fa5affSMarcin Spiewak } 162465fa5affSMarcin Spiewak 1625045c781dSRichael Zhuang printf("Running I/O for %" PRIu64 " seconds...\n", g_time_in_usec / (uint64_t)SPDK_SEC_TO_USEC); 162629784f35SKrzysztof Karas fflush(stdout); 162729784f35SKrzysztof Karas 162829784f35SKrzysztof Karas /* Start a timer to dump performance numbers */ 162929784f35SKrzysztof Karas g_start_tsc = spdk_get_ticks(); 163027134348SAnisa Su if (!g_summarize_performance) { 1631723dd06eSRichael Zhuang printf("%*s\n", 107, "Latency(us)"); 1632723dd06eSRichael Zhuang printf("\r %-*s: %10s %10s %10s %10s %8s %10s %10s %10s\n", 1633723dd06eSRichael Zhuang 28, "Device Information", "runtime(s)", "IOPS", "MiB/s", "Fail/s", "TO/s", "Average", "min", "max"); 163427134348SAnisa Su } 163527134348SAnisa Su if (!g_perf_timer) { 163629784f35SKrzysztof Karas g_perf_timer = SPDK_POLLER_REGISTER(performance_statistics_thread, NULL, 163729784f35SKrzysztof Karas g_show_performance_period_in_usec); 163829784f35SKrzysztof Karas } 163929784f35SKrzysztof Karas 164029784f35SKrzysztof Karas /* Iterate jobs to start all I/O */ 164129784f35SKrzysztof Karas TAILQ_FOREACH(job, &g_bdevperf.jobs, link) { 16427cc16c96SShuhei Matsumoto spdk_bdev_set_timeout(job->bdev_desc, g_timeout_in_sec, bdevperf_timeout_cb, job); 16437cc16c96SShuhei Matsumoto 164429784f35SKrzysztof Karas g_bdevperf.running_jobs++; 164529784f35SKrzysztof Karas spdk_thread_send_msg(job->thread, bdevperf_job_run, job); 164629784f35SKrzysztof Karas } 164729784f35SKrzysztof Karas } 164829784f35SKrzysztof Karas 164929784f35SKrzysztof Karas static void 16507cc16c96SShuhei Matsumoto _bdevperf_job_drain(void *ctx) 16517cc16c96SShuhei Matsumoto { 16527cc16c96SShuhei Matsumoto bdevperf_job_drain(ctx); 16537cc16c96SShuhei Matsumoto } 16547cc16c96SShuhei Matsumoto 16557cc16c96SShuhei Matsumoto static void 165629784f35SKrzysztof Karas bdevperf_bdev_removed(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx) 165729784f35SKrzysztof Karas { 165829784f35SKrzysztof Karas struct bdevperf_job *job = event_ctx; 165929784f35SKrzysztof Karas 166029784f35SKrzysztof Karas if (SPDK_BDEV_EVENT_REMOVE == type) { 16617cc16c96SShuhei Matsumoto spdk_thread_send_msg(job->thread, _bdevperf_job_drain, job); 166229784f35SKrzysztof Karas } 166329784f35SKrzysztof Karas } 166429784f35SKrzysztof Karas 1665723dd06eSRichael Zhuang static void 1666723dd06eSRichael Zhuang bdevperf_histogram_status_cb(void *cb_arg, int status) 1667723dd06eSRichael Zhuang { 1668723dd06eSRichael Zhuang if (status != 0) { 1669723dd06eSRichael Zhuang g_run_rc = status; 1670723dd06eSRichael Zhuang if (g_continue_on_failure == false) { 1671723dd06eSRichael Zhuang g_error_to_exit = true; 1672723dd06eSRichael Zhuang } 1673723dd06eSRichael Zhuang } 1674723dd06eSRichael Zhuang 1675723dd06eSRichael Zhuang if (--g_bdev_count == 0) { 1676723dd06eSRichael Zhuang if (g_run_rc == 0) { 1677723dd06eSRichael Zhuang /* Ready to run the test */ 1678723dd06eSRichael Zhuang bdevperf_test(); 1679723dd06eSRichael Zhuang } else { 1680723dd06eSRichael Zhuang bdevperf_test_done(NULL); 1681723dd06eSRichael Zhuang } 1682723dd06eSRichael Zhuang } 1683723dd06eSRichael Zhuang } 1684723dd06eSRichael Zhuang 168529784f35SKrzysztof Karas static uint32_t g_construct_job_count = 0; 168629784f35SKrzysztof Karas 168756479486SShuhei Matsumoto static int 168856479486SShuhei Matsumoto _bdevperf_enable_histogram(void *ctx, struct spdk_bdev *bdev) 168956479486SShuhei Matsumoto { 169056479486SShuhei Matsumoto bool *enable = ctx; 169156479486SShuhei Matsumoto 169256479486SShuhei Matsumoto g_bdev_count++; 169356479486SShuhei Matsumoto 169456479486SShuhei Matsumoto spdk_bdev_histogram_enable(bdev, bdevperf_histogram_status_cb, NULL, *enable); 169556479486SShuhei Matsumoto 169656479486SShuhei Matsumoto return 0; 169756479486SShuhei Matsumoto } 169856479486SShuhei Matsumoto 169929784f35SKrzysztof Karas static void 170056479486SShuhei Matsumoto bdevperf_enable_histogram(bool enable) 1701723dd06eSRichael Zhuang { 1702723dd06eSRichael Zhuang struct spdk_bdev *bdev; 170356479486SShuhei Matsumoto int rc; 170456479486SShuhei Matsumoto 1705723dd06eSRichael Zhuang /* increment initial g_bdev_count so that it will never reach 0 in the middle of iteration */ 1706723dd06eSRichael Zhuang g_bdev_count = 1; 1707723dd06eSRichael Zhuang 1708723dd06eSRichael Zhuang if (g_job_bdev_name != NULL) { 1709723dd06eSRichael Zhuang bdev = spdk_bdev_get_by_name(g_job_bdev_name); 1710723dd06eSRichael Zhuang if (bdev) { 171156479486SShuhei Matsumoto rc = _bdevperf_enable_histogram(&enable, bdev); 1712723dd06eSRichael Zhuang } else { 1713723dd06eSRichael Zhuang fprintf(stderr, "Unable to find bdev '%s'\n", g_job_bdev_name); 171456479486SShuhei Matsumoto rc = -1; 1715723dd06eSRichael Zhuang } 1716723dd06eSRichael Zhuang } else { 171756479486SShuhei Matsumoto rc = spdk_for_each_bdev_leaf(&enable, _bdevperf_enable_histogram); 1718723dd06eSRichael Zhuang } 1719723dd06eSRichael Zhuang 172056479486SShuhei Matsumoto bdevperf_histogram_status_cb(NULL, rc); 1721723dd06eSRichael Zhuang } 1722723dd06eSRichael Zhuang 1723723dd06eSRichael Zhuang static void 172429784f35SKrzysztof Karas _bdevperf_construct_job_done(void *ctx) 172529784f35SKrzysztof Karas { 172629784f35SKrzysztof Karas if (--g_construct_job_count == 0) { 172729784f35SKrzysztof Karas if (g_run_rc != 0) { 172829784f35SKrzysztof Karas /* Something failed. */ 172929784f35SKrzysztof Karas bdevperf_test_done(NULL); 173029784f35SKrzysztof Karas return; 173129784f35SKrzysztof Karas } 173229784f35SKrzysztof Karas 1733723dd06eSRichael Zhuang /* always enable histogram. */ 173456479486SShuhei Matsumoto bdevperf_enable_histogram(true); 173529784f35SKrzysztof Karas } else if (g_run_rc != 0) { 173629784f35SKrzysztof Karas /* Reset error as some jobs constructed right */ 173729784f35SKrzysztof Karas g_run_rc = 0; 173829784f35SKrzysztof Karas if (g_continue_on_failure == false) { 173929784f35SKrzysztof Karas g_error_to_exit = true; 174029784f35SKrzysztof Karas } 174129784f35SKrzysztof Karas } 174229784f35SKrzysztof Karas } 174329784f35SKrzysztof Karas 174429784f35SKrzysztof Karas /* Checkformat will not allow to use inlined type, 174529784f35SKrzysztof Karas this is a workaround */ 174629784f35SKrzysztof Karas typedef struct spdk_thread *spdk_thread_t; 174729784f35SKrzysztof Karas 174829784f35SKrzysztof Karas static spdk_thread_t 174929784f35SKrzysztof Karas construct_job_thread(struct spdk_cpuset *cpumask, const char *tag) 175029784f35SKrzysztof Karas { 175129784f35SKrzysztof Karas struct spdk_cpuset tmp; 175229784f35SKrzysztof Karas 175329784f35SKrzysztof Karas /* This function runs on the main thread. */ 175429784f35SKrzysztof Karas assert(g_main_thread == spdk_get_thread()); 175529784f35SKrzysztof Karas 175629784f35SKrzysztof Karas /* Handle default mask */ 175729784f35SKrzysztof Karas if (spdk_cpuset_count(cpumask) == 0) { 175829784f35SKrzysztof Karas cpumask = &g_all_cpuset; 175929784f35SKrzysztof Karas } 176029784f35SKrzysztof Karas 176129784f35SKrzysztof Karas /* Warn user that mask might need to be changed */ 176229784f35SKrzysztof Karas spdk_cpuset_copy(&tmp, cpumask); 176329784f35SKrzysztof Karas spdk_cpuset_or(&tmp, &g_all_cpuset); 176429784f35SKrzysztof Karas if (!spdk_cpuset_equal(&tmp, &g_all_cpuset)) { 176529784f35SKrzysztof Karas fprintf(stderr, "cpumask for '%s' is too big\n", tag); 176629784f35SKrzysztof Karas } 176729784f35SKrzysztof Karas 176829784f35SKrzysztof Karas return spdk_thread_create(tag, cpumask); 176929784f35SKrzysztof Karas } 177029784f35SKrzysztof Karas 177129784f35SKrzysztof Karas static uint32_t 177229784f35SKrzysztof Karas _get_next_core(void) 177329784f35SKrzysztof Karas { 177429784f35SKrzysztof Karas static uint32_t current_core = SPDK_ENV_LCORE_ID_ANY; 177529784f35SKrzysztof Karas 177629784f35SKrzysztof Karas if (current_core == SPDK_ENV_LCORE_ID_ANY) { 177729784f35SKrzysztof Karas current_core = spdk_env_get_first_core(); 177829784f35SKrzysztof Karas return current_core; 177929784f35SKrzysztof Karas } 178029784f35SKrzysztof Karas 178129784f35SKrzysztof Karas current_core = spdk_env_get_next_core(current_core); 178229784f35SKrzysztof Karas if (current_core == SPDK_ENV_LCORE_ID_ANY) { 178329784f35SKrzysztof Karas current_core = spdk_env_get_first_core(); 178429784f35SKrzysztof Karas } 178529784f35SKrzysztof Karas 178629784f35SKrzysztof Karas return current_core; 178729784f35SKrzysztof Karas } 178829784f35SKrzysztof Karas 178929784f35SKrzysztof Karas static void 179029784f35SKrzysztof Karas _bdevperf_construct_job(void *ctx) 179129784f35SKrzysztof Karas { 179229784f35SKrzysztof Karas struct bdevperf_job *job = ctx; 179329784f35SKrzysztof Karas 179429784f35SKrzysztof Karas if (g_zcopy) { 179529784f35SKrzysztof Karas if (!spdk_bdev_io_type_supported(job->bdev, SPDK_BDEV_IO_TYPE_ZCOPY)) { 179629784f35SKrzysztof Karas printf("Test requires ZCOPY but bdev module does not support ZCOPY\n"); 179729784f35SKrzysztof Karas g_run_rc = -ENOTSUP; 179829784f35SKrzysztof Karas goto end; 179929784f35SKrzysztof Karas } 180029784f35SKrzysztof Karas } 180129784f35SKrzysztof Karas 180229784f35SKrzysztof Karas job->ch = spdk_bdev_get_io_channel(job->bdev_desc); 180329784f35SKrzysztof Karas if (!job->ch) { 18047cc16c96SShuhei Matsumoto SPDK_ERRLOG("Could not get io_channel for device %s\n", spdk_bdev_get_name(job->bdev)); 180529784f35SKrzysztof Karas g_run_rc = -ENOMEM; 180629784f35SKrzysztof Karas goto end; 180729784f35SKrzysztof Karas } 180829784f35SKrzysztof Karas 180929784f35SKrzysztof Karas end: 181029784f35SKrzysztof Karas spdk_thread_send_msg(g_main_thread, _bdevperf_construct_job_done, NULL); 181129784f35SKrzysztof Karas } 181229784f35SKrzysztof Karas 181329784f35SKrzysztof Karas static void 181429784f35SKrzysztof Karas job_init_rw(struct bdevperf_job *job, enum job_config_rw rw) 181529784f35SKrzysztof Karas { 181629784f35SKrzysztof Karas switch (rw) { 181729784f35SKrzysztof Karas case JOB_CONFIG_RW_READ: 181829784f35SKrzysztof Karas job->rw_percentage = 100; 181929784f35SKrzysztof Karas break; 182029784f35SKrzysztof Karas case JOB_CONFIG_RW_WRITE: 182129784f35SKrzysztof Karas job->rw_percentage = 0; 182229784f35SKrzysztof Karas break; 182329784f35SKrzysztof Karas case JOB_CONFIG_RW_RANDREAD: 182429784f35SKrzysztof Karas job->is_random = true; 182529784f35SKrzysztof Karas job->rw_percentage = 100; 182629784f35SKrzysztof Karas job->seed = rand(); 182729784f35SKrzysztof Karas break; 182829784f35SKrzysztof Karas case JOB_CONFIG_RW_RANDWRITE: 182929784f35SKrzysztof Karas job->is_random = true; 183029784f35SKrzysztof Karas job->rw_percentage = 0; 183129784f35SKrzysztof Karas job->seed = rand(); 183229784f35SKrzysztof Karas break; 183329784f35SKrzysztof Karas case JOB_CONFIG_RW_RW: 183429784f35SKrzysztof Karas job->is_random = false; 183529784f35SKrzysztof Karas break; 183629784f35SKrzysztof Karas case JOB_CONFIG_RW_RANDRW: 183729784f35SKrzysztof Karas job->is_random = true; 183829784f35SKrzysztof Karas job->seed = rand(); 183929784f35SKrzysztof Karas break; 1840620e9a1eSTomasz Zawadzki case JOB_CONFIG_RW_RESET: 1841620e9a1eSTomasz Zawadzki /* Reset shares the flow with verify. */ 1842620e9a1eSTomasz Zawadzki job->reset = true; 1843620e9a1eSTomasz Zawadzki /* fallthrough */ 184429784f35SKrzysztof Karas case JOB_CONFIG_RW_VERIFY: 184529784f35SKrzysztof Karas job->verify = true; 1846b2cfb6b4SJacek Kalwas /* For verify flow read is done on write completion 1847b2cfb6b4SJacek Kalwas * callback only, rw_percentage shall not be used. */ 1848b2cfb6b4SJacek Kalwas job->rw_percentage = 0; 184929784f35SKrzysztof Karas break; 185029784f35SKrzysztof Karas case JOB_CONFIG_RW_UNMAP: 185129784f35SKrzysztof Karas job->unmap = true; 185229784f35SKrzysztof Karas break; 185329784f35SKrzysztof Karas case JOB_CONFIG_RW_FLUSH: 185429784f35SKrzysztof Karas job->flush = true; 185529784f35SKrzysztof Karas break; 185629784f35SKrzysztof Karas case JOB_CONFIG_RW_WRITE_ZEROES: 185729784f35SKrzysztof Karas job->write_zeroes = true; 185829784f35SKrzysztof Karas break; 185929784f35SKrzysztof Karas } 186029784f35SKrzysztof Karas } 186129784f35SKrzysztof Karas 186229784f35SKrzysztof Karas static int 186329784f35SKrzysztof Karas bdevperf_construct_job(struct spdk_bdev *bdev, struct job_config *config, 186429784f35SKrzysztof Karas struct spdk_thread *thread) 186529784f35SKrzysztof Karas { 186629784f35SKrzysztof Karas struct bdevperf_job *job; 1867*971ec012SShuhei Matsumoto struct spdk_bdev_open_opts opts = {}; 186829784f35SKrzysztof Karas struct bdevperf_task *task; 186929784f35SKrzysztof Karas int block_size, data_block_size; 187029784f35SKrzysztof Karas int rc; 187129784f35SKrzysztof Karas int task_num, n; 187265fb818aSJim Harris int32_t numa_id; 187329784f35SKrzysztof Karas 187429784f35SKrzysztof Karas job = calloc(1, sizeof(struct bdevperf_job)); 187529784f35SKrzysztof Karas if (!job) { 187629784f35SKrzysztof Karas fprintf(stderr, "Unable to allocate memory for new job.\n"); 187729784f35SKrzysztof Karas return -ENOMEM; 187829784f35SKrzysztof Karas } 187929784f35SKrzysztof Karas 18807cc16c96SShuhei Matsumoto job->thread = thread; 18817cc16c96SShuhei Matsumoto 188229784f35SKrzysztof Karas job->name = strdup(spdk_bdev_get_name(bdev)); 188329784f35SKrzysztof Karas if (!job->name) { 188429784f35SKrzysztof Karas fprintf(stderr, "Unable to allocate memory for job name.\n"); 1885dcb296a3SJim Harris bdevperf_job_free(job); 188629784f35SKrzysztof Karas return -ENOMEM; 188729784f35SKrzysztof Karas } 188829784f35SKrzysztof Karas 1889*971ec012SShuhei Matsumoto spdk_bdev_open_opts_init(&opts, sizeof(opts)); 1890*971ec012SShuhei Matsumoto opts.hide_metadata = g_hide_metadata; 1891*971ec012SShuhei Matsumoto 1892*971ec012SShuhei Matsumoto rc = spdk_bdev_open_ext_v2(job->name, true, bdevperf_bdev_removed, job, &opts, 1893*971ec012SShuhei Matsumoto &job->bdev_desc); 18947cc16c96SShuhei Matsumoto if (rc != 0) { 18957cc16c96SShuhei Matsumoto fprintf(stderr, "Could not open leaf bdev %s, error=%d\n", job->name, rc); 18967cc16c96SShuhei Matsumoto bdevperf_job_free(job); 18977cc16c96SShuhei Matsumoto return rc; 18987cc16c96SShuhei Matsumoto } 18997cc16c96SShuhei Matsumoto 1900894d5af2SShuhei Matsumoto block_size = spdk_bdev_desc_get_block_size(job->bdev_desc); 1901894d5af2SShuhei Matsumoto data_block_size = spdk_bdev_get_data_block_size(bdev); 1902894d5af2SShuhei Matsumoto 19034348ccc0SEugene Kobyak job->workload_type = config->rw; 190429784f35SKrzysztof Karas job->io_size = config->bs; 190529784f35SKrzysztof Karas job->rw_percentage = config->rwmixread; 190629784f35SKrzysztof Karas job->continue_on_failure = g_continue_on_failure; 190729784f35SKrzysztof Karas job->queue_depth = config->iodepth; 190829784f35SKrzysztof Karas job->bdev = bdev; 190929784f35SKrzysztof Karas job->io_size_blocks = job->io_size / data_block_size; 191029784f35SKrzysztof Karas job->buf_size = job->io_size_blocks * block_size; 191129784f35SKrzysztof Karas job->abort = g_abort; 191229784f35SKrzysztof Karas job_init_rw(job, config->rw); 1913075fb5b8SShuhei Matsumoto job->md_check = spdk_bdev_get_dif_type(job->bdev) == SPDK_DIF_DISABLE; 191429784f35SKrzysztof Karas 191529784f35SKrzysztof Karas if ((job->io_size % data_block_size) != 0) { 191629784f35SKrzysztof Karas SPDK_ERRLOG("IO size (%d) is not multiples of data block size of bdev %s (%"PRIu32")\n", 191729784f35SKrzysztof Karas job->io_size, spdk_bdev_get_name(bdev), data_block_size); 1918dcb296a3SJim Harris bdevperf_job_free(job); 191929784f35SKrzysztof Karas return -ENOTSUP; 192029784f35SKrzysztof Karas } 192129784f35SKrzysztof Karas 192229784f35SKrzysztof Karas if (job->unmap && !spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP)) { 192329784f35SKrzysztof Karas printf("Skipping %s because it does not support unmap\n", spdk_bdev_get_name(bdev)); 1924dcb296a3SJim Harris bdevperf_job_free(job); 192529784f35SKrzysztof Karas return -ENOTSUP; 192629784f35SKrzysztof Karas } 192729784f35SKrzysztof Karas 1928894d5af2SShuhei Matsumoto if (spdk_bdev_desc_is_dif_check_enabled(job->bdev_desc, SPDK_DIF_CHECK_TYPE_REFTAG)) { 192929784f35SKrzysztof Karas job->dif_check_flags |= SPDK_DIF_FLAGS_REFTAG_CHECK; 193029784f35SKrzysztof Karas } 1931894d5af2SShuhei Matsumoto if (spdk_bdev_desc_is_dif_check_enabled(job->bdev_desc, SPDK_DIF_CHECK_TYPE_GUARD)) { 193229784f35SKrzysztof Karas job->dif_check_flags |= SPDK_DIF_FLAGS_GUARD_CHECK; 193329784f35SKrzysztof Karas } 193429784f35SKrzysztof Karas 193529784f35SKrzysztof Karas job->offset_in_ios = 0; 193629784f35SKrzysztof Karas 193729784f35SKrzysztof Karas if (config->length != 0) { 193829784f35SKrzysztof Karas /* Use subset of disk */ 193929784f35SKrzysztof Karas job->size_in_ios = config->length / job->io_size_blocks; 194029784f35SKrzysztof Karas job->ios_base = config->offset / job->io_size_blocks; 194129784f35SKrzysztof Karas } else { 194229784f35SKrzysztof Karas /* Use whole disk */ 194329784f35SKrzysztof Karas job->size_in_ios = spdk_bdev_get_num_blocks(bdev) / job->io_size_blocks; 194429784f35SKrzysztof Karas job->ios_base = 0; 194529784f35SKrzysztof Karas } 194629784f35SKrzysztof Karas 194729784f35SKrzysztof Karas if (job->is_random && g_zipf_theta > 0) { 194829784f35SKrzysztof Karas job->zipf = spdk_zipf_create(job->size_in_ios, g_zipf_theta, 0); 194929784f35SKrzysztof Karas } 195029784f35SKrzysztof Karas 195129784f35SKrzysztof Karas if (job->verify) { 1952652db802SSlawomir Ptak if (job->size_in_ios >= UINT32_MAX) { 1953652db802SSlawomir Ptak SPDK_ERRLOG("Due to constraints of verify operation, the job storage capacity is too large\n"); 1954652db802SSlawomir Ptak bdevperf_job_free(job); 1955652db802SSlawomir Ptak return -ENOMEM; 1956652db802SSlawomir Ptak } 195729784f35SKrzysztof Karas job->outstanding = spdk_bit_array_create(job->size_in_ios); 195829784f35SKrzysztof Karas if (job->outstanding == NULL) { 195929784f35SKrzysztof Karas SPDK_ERRLOG("Could not create outstanding array bitmap for bdev %s\n", 196029784f35SKrzysztof Karas spdk_bdev_get_name(bdev)); 1961dcb296a3SJim Harris bdevperf_job_free(job); 196229784f35SKrzysztof Karas return -ENOMEM; 196329784f35SKrzysztof Karas } 1964e58885f9SAlexey Marchuk if (job->queue_depth > (int)job->size_in_ios) { 1965e58885f9SAlexey Marchuk SPDK_WARNLOG("Due to constraints of verify job, queue depth (-q, %d) can't exceed the number of IO " 1966e58885f9SAlexey Marchuk "requests which can be submitted to the bdev %s simultaneously (%"PRIu64"). " 1967e58885f9SAlexey Marchuk "Queue depth is limited to %"PRIu64"\n", 1968e58885f9SAlexey Marchuk job->queue_depth, job->name, job->size_in_ios, job->size_in_ios); 1969e58885f9SAlexey Marchuk job->queue_depth = (int)job->size_in_ios; 1970e58885f9SAlexey Marchuk } 197129784f35SKrzysztof Karas } 197229784f35SKrzysztof Karas 1973723dd06eSRichael Zhuang job->histogram = spdk_histogram_data_alloc(); 1974723dd06eSRichael Zhuang if (job->histogram == NULL) { 1975723dd06eSRichael Zhuang fprintf(stderr, "Failed to allocate histogram\n"); 1976723dd06eSRichael Zhuang bdevperf_job_free(job); 1977723dd06eSRichael Zhuang return -ENOMEM; 1978723dd06eSRichael Zhuang } 1979723dd06eSRichael Zhuang 198029784f35SKrzysztof Karas TAILQ_INIT(&job->task_list); 198129784f35SKrzysztof Karas 1982d254a3b9SSlawomir Ptak if (g_random_map) { 1983d254a3b9SSlawomir Ptak if (job->size_in_ios >= UINT32_MAX) { 1984d254a3b9SSlawomir Ptak SPDK_ERRLOG("Due to constraints of the random map, the job storage capacity is too large\n"); 1985d254a3b9SSlawomir Ptak bdevperf_job_free(job); 1986d254a3b9SSlawomir Ptak return -ENOMEM; 1987d254a3b9SSlawomir Ptak } 1988d254a3b9SSlawomir Ptak job->random_map = spdk_bit_array_create(job->size_in_ios); 1989d254a3b9SSlawomir Ptak if (job->random_map == NULL) { 1990d254a3b9SSlawomir Ptak SPDK_ERRLOG("Could not create random_map array bitmap for bdev %s\n", 1991d254a3b9SSlawomir Ptak spdk_bdev_get_name(bdev)); 1992d254a3b9SSlawomir Ptak bdevperf_job_free(job); 1993d254a3b9SSlawomir Ptak return -ENOMEM; 1994d254a3b9SSlawomir Ptak } 1995d254a3b9SSlawomir Ptak } 1996d254a3b9SSlawomir Ptak 199729784f35SKrzysztof Karas task_num = job->queue_depth; 199829784f35SKrzysztof Karas if (job->reset) { 199929784f35SKrzysztof Karas task_num += 1; 200029784f35SKrzysztof Karas } 200129784f35SKrzysztof Karas if (job->abort) { 200229784f35SKrzysztof Karas task_num += job->queue_depth; 200329784f35SKrzysztof Karas } 200429784f35SKrzysztof Karas 200529784f35SKrzysztof Karas TAILQ_INSERT_TAIL(&g_bdevperf.jobs, job, link); 200629784f35SKrzysztof Karas 200765fb818aSJim Harris numa_id = spdk_bdev_get_numa_id(job->bdev); 200865fb818aSJim Harris 200929784f35SKrzysztof Karas for (n = 0; n < task_num; n++) { 201029784f35SKrzysztof Karas task = calloc(1, sizeof(struct bdevperf_task)); 201129784f35SKrzysztof Karas if (!task) { 201229784f35SKrzysztof Karas fprintf(stderr, "Failed to allocate task from memory\n"); 20138e8c360bSKrzysztof Karas spdk_zipf_free(&job->zipf); 201429784f35SKrzysztof Karas return -ENOMEM; 201529784f35SKrzysztof Karas } 201629784f35SKrzysztof Karas 201729784f35SKrzysztof Karas task->buf = spdk_zmalloc(job->buf_size, spdk_bdev_get_buf_align(job->bdev), NULL, 201865fb818aSJim Harris numa_id, SPDK_MALLOC_DMA); 201929784f35SKrzysztof Karas if (!task->buf) { 202029784f35SKrzysztof Karas fprintf(stderr, "Cannot allocate buf for task=%p\n", task); 20218e8c360bSKrzysztof Karas spdk_zipf_free(&job->zipf); 202229784f35SKrzysztof Karas free(task); 202329784f35SKrzysztof Karas return -ENOMEM; 202429784f35SKrzysztof Karas } 202529784f35SKrzysztof Karas 2026aa824ae6SOlivier Mayer if (job->verify && job->buf_size > SPDK_BDEV_LARGE_BUF_MAX_SIZE) { 2027aa824ae6SOlivier Mayer task->verify_buf = spdk_zmalloc(job->buf_size, spdk_bdev_get_buf_align(job->bdev), NULL, 202865fb818aSJim Harris numa_id, SPDK_MALLOC_DMA); 2029aa824ae6SOlivier Mayer if (!task->verify_buf) { 2030aa824ae6SOlivier Mayer fprintf(stderr, "Cannot allocate buf_verify for task=%p\n", task); 2031aa824ae6SOlivier Mayer spdk_free(task->buf); 2032aa824ae6SOlivier Mayer spdk_zipf_free(&job->zipf); 2033aa824ae6SOlivier Mayer free(task); 2034aa824ae6SOlivier Mayer return -ENOMEM; 2035aa824ae6SOlivier Mayer } 2036aa824ae6SOlivier Mayer 2037dc3ea9d2SSlawomir Ptak if (spdk_bdev_is_md_separate(job->bdev)) { 2038dc3ea9d2SSlawomir Ptak task->verify_md_buf = spdk_zmalloc(spdk_bdev_get_md_size(bdev) * job->io_size_blocks, 2039dc3ea9d2SSlawomir Ptak spdk_bdev_get_buf_align(job->bdev), NULL, numa_id, SPDK_MALLOC_DMA); 2040dc3ea9d2SSlawomir Ptak if (!task->verify_md_buf) { 2041dc3ea9d2SSlawomir Ptak fprintf(stderr, "Cannot allocate verify_md_buf for task=%p\n", task); 2042dc3ea9d2SSlawomir Ptak spdk_free(task->buf); 2043dc3ea9d2SSlawomir Ptak spdk_free(task->verify_buf); 2044dc3ea9d2SSlawomir Ptak spdk_zipf_free(&job->zipf); 2045dc3ea9d2SSlawomir Ptak free(task); 2046dc3ea9d2SSlawomir Ptak return -ENOMEM; 2047dc3ea9d2SSlawomir Ptak } 2048dc3ea9d2SSlawomir Ptak } 2049aa824ae6SOlivier Mayer } 2050aa824ae6SOlivier Mayer 2051894d5af2SShuhei Matsumoto if (spdk_bdev_desc_is_md_separate(job->bdev_desc)) { 205229784f35SKrzysztof Karas task->md_buf = spdk_zmalloc(job->io_size_blocks * 2053894d5af2SShuhei Matsumoto spdk_bdev_desc_get_md_size(job->bdev_desc), 0, NULL, 205465fb818aSJim Harris numa_id, SPDK_MALLOC_DMA); 205529784f35SKrzysztof Karas if (!task->md_buf) { 205629784f35SKrzysztof Karas fprintf(stderr, "Cannot allocate md buf for task=%p\n", task); 20578e8c360bSKrzysztof Karas spdk_zipf_free(&job->zipf); 2058aa824ae6SOlivier Mayer spdk_free(task->verify_buf); 2059dc3ea9d2SSlawomir Ptak spdk_free(task->verify_md_buf); 206029784f35SKrzysztof Karas spdk_free(task->buf); 206129784f35SKrzysztof Karas free(task); 206229784f35SKrzysztof Karas return -ENOMEM; 206329784f35SKrzysztof Karas } 206429784f35SKrzysztof Karas } 206529784f35SKrzysztof Karas 206629784f35SKrzysztof Karas task->job = job; 206729784f35SKrzysztof Karas TAILQ_INSERT_TAIL(&job->task_list, task, link); 206829784f35SKrzysztof Karas } 206929784f35SKrzysztof Karas 207029784f35SKrzysztof Karas g_construct_job_count++; 207129784f35SKrzysztof Karas 207229784f35SKrzysztof Karas rc = spdk_thread_send_msg(thread, _bdevperf_construct_job, job); 207329784f35SKrzysztof Karas assert(rc == 0); 207429784f35SKrzysztof Karas 207529784f35SKrzysztof Karas return rc; 207629784f35SKrzysztof Karas } 207729784f35SKrzysztof Karas 207829784f35SKrzysztof Karas static int 207929784f35SKrzysztof Karas parse_rw(const char *str, enum job_config_rw ret) 208029784f35SKrzysztof Karas { 208129784f35SKrzysztof Karas if (str == NULL) { 208229784f35SKrzysztof Karas return ret; 208329784f35SKrzysztof Karas } 208429784f35SKrzysztof Karas 208529784f35SKrzysztof Karas if (!strcmp(str, "read")) { 208629784f35SKrzysztof Karas ret = JOB_CONFIG_RW_READ; 208729784f35SKrzysztof Karas } else if (!strcmp(str, "randread")) { 208829784f35SKrzysztof Karas ret = JOB_CONFIG_RW_RANDREAD; 208929784f35SKrzysztof Karas } else if (!strcmp(str, "write")) { 209029784f35SKrzysztof Karas ret = JOB_CONFIG_RW_WRITE; 209129784f35SKrzysztof Karas } else if (!strcmp(str, "randwrite")) { 209229784f35SKrzysztof Karas ret = JOB_CONFIG_RW_RANDWRITE; 209329784f35SKrzysztof Karas } else if (!strcmp(str, "verify")) { 209429784f35SKrzysztof Karas ret = JOB_CONFIG_RW_VERIFY; 209529784f35SKrzysztof Karas } else if (!strcmp(str, "reset")) { 209629784f35SKrzysztof Karas ret = JOB_CONFIG_RW_RESET; 209729784f35SKrzysztof Karas } else if (!strcmp(str, "unmap")) { 209829784f35SKrzysztof Karas ret = JOB_CONFIG_RW_UNMAP; 209929784f35SKrzysztof Karas } else if (!strcmp(str, "write_zeroes")) { 210029784f35SKrzysztof Karas ret = JOB_CONFIG_RW_WRITE_ZEROES; 210129784f35SKrzysztof Karas } else if (!strcmp(str, "flush")) { 210229784f35SKrzysztof Karas ret = JOB_CONFIG_RW_FLUSH; 210329784f35SKrzysztof Karas } else if (!strcmp(str, "rw")) { 210429784f35SKrzysztof Karas ret = JOB_CONFIG_RW_RW; 210529784f35SKrzysztof Karas } else if (!strcmp(str, "randrw")) { 210629784f35SKrzysztof Karas ret = JOB_CONFIG_RW_RANDRW; 210729784f35SKrzysztof Karas } else { 210829784f35SKrzysztof Karas fprintf(stderr, "rw must be one of\n" 210932e5ce31SAmir Haroush PATTERN_TYPES_STR "\n"); 211029784f35SKrzysztof Karas ret = BDEVPERF_CONFIG_ERROR; 211129784f35SKrzysztof Karas } 211229784f35SKrzysztof Karas 211329784f35SKrzysztof Karas return ret; 211429784f35SKrzysztof Karas } 211529784f35SKrzysztof Karas 211629784f35SKrzysztof Karas static const char * 211729784f35SKrzysztof Karas config_filename_next(const char *filename, char *out) 211829784f35SKrzysztof Karas { 211929784f35SKrzysztof Karas int i, k; 212029784f35SKrzysztof Karas 212129784f35SKrzysztof Karas if (filename == NULL) { 212229784f35SKrzysztof Karas out[0] = '\0'; 212329784f35SKrzysztof Karas return NULL; 212429784f35SKrzysztof Karas } 212529784f35SKrzysztof Karas 212629784f35SKrzysztof Karas if (filename[0] == ':') { 212729784f35SKrzysztof Karas filename++; 212829784f35SKrzysztof Karas } 212929784f35SKrzysztof Karas 213029784f35SKrzysztof Karas for (i = 0, k = 0; 213129784f35SKrzysztof Karas filename[i] != '\0' && 213229784f35SKrzysztof Karas filename[i] != ':' && 21330a638e9dSMarcin Spiewak i < BDEVPERF_CONFIG_MAX_FILENAME && 21340a638e9dSMarcin Spiewak k < (BDEVPERF_CONFIG_MAX_FILENAME - 1); 213529784f35SKrzysztof Karas i++) { 213629784f35SKrzysztof Karas if (filename[i] == ' ' || filename[i] == '\t') { 213729784f35SKrzysztof Karas continue; 213829784f35SKrzysztof Karas } 213929784f35SKrzysztof Karas 214029784f35SKrzysztof Karas out[k++] = filename[i]; 214129784f35SKrzysztof Karas } 214229784f35SKrzysztof Karas out[k] = 0; 214329784f35SKrzysztof Karas 214429784f35SKrzysztof Karas return filename + i; 214529784f35SKrzysztof Karas } 214629784f35SKrzysztof Karas 21478e373044SShuhei Matsumoto static struct spdk_thread * 21488e373044SShuhei Matsumoto get_lcore_thread(uint32_t lcore) 21498e373044SShuhei Matsumoto { 21508e373044SShuhei Matsumoto struct lcore_thread *lthread; 21518e373044SShuhei Matsumoto 21528e373044SShuhei Matsumoto TAILQ_FOREACH(lthread, &g_lcore_thread_list, link) { 21538e373044SShuhei Matsumoto if (lthread->lcore == lcore) { 21548e373044SShuhei Matsumoto return lthread->thread; 21558e373044SShuhei Matsumoto } 21568e373044SShuhei Matsumoto } 21578e373044SShuhei Matsumoto 21588e373044SShuhei Matsumoto return NULL; 21598e373044SShuhei Matsumoto } 21608e373044SShuhei Matsumoto 216129784f35SKrzysztof Karas static void 21622b62e64dSKonrad Sztyber create_lcore_thread(uint32_t lcore) 21632b62e64dSKonrad Sztyber { 21642b62e64dSKonrad Sztyber struct lcore_thread *lthread; 21652b62e64dSKonrad Sztyber struct spdk_cpuset cpumask = {}; 21662b62e64dSKonrad Sztyber char name[32]; 21672b62e64dSKonrad Sztyber 21682b62e64dSKonrad Sztyber lthread = calloc(1, sizeof(*lthread)); 21692b62e64dSKonrad Sztyber assert(lthread != NULL); 21702b62e64dSKonrad Sztyber 21712b62e64dSKonrad Sztyber lthread->lcore = lcore; 21722b62e64dSKonrad Sztyber 21732b62e64dSKonrad Sztyber snprintf(name, sizeof(name), "lcore_%u", lcore); 21742b62e64dSKonrad Sztyber spdk_cpuset_set_cpu(&cpumask, lcore, true); 21752b62e64dSKonrad Sztyber 21762b62e64dSKonrad Sztyber lthread->thread = spdk_thread_create(name, &cpumask); 21772b62e64dSKonrad Sztyber assert(lthread->thread != NULL); 21782b62e64dSKonrad Sztyber 21792b62e64dSKonrad Sztyber TAILQ_INSERT_TAIL(&g_lcore_thread_list, lthread, link); 21802b62e64dSKonrad Sztyber } 21812b62e64dSKonrad Sztyber 21822b62e64dSKonrad Sztyber static void 218329784f35SKrzysztof Karas bdevperf_construct_jobs(void) 218429784f35SKrzysztof Karas { 218529784f35SKrzysztof Karas char filename[BDEVPERF_CONFIG_MAX_FILENAME]; 218629784f35SKrzysztof Karas struct spdk_thread *thread; 218729784f35SKrzysztof Karas struct job_config *config; 218829784f35SKrzysztof Karas struct spdk_bdev *bdev; 218929784f35SKrzysztof Karas const char *filenames; 21902b62e64dSKonrad Sztyber uint32_t i; 219129784f35SKrzysztof Karas int rc; 219229784f35SKrzysztof Karas 21932b62e64dSKonrad Sztyber if (g_one_thread_per_lcore) { 21942b62e64dSKonrad Sztyber SPDK_ENV_FOREACH_CORE(i) { 21952b62e64dSKonrad Sztyber create_lcore_thread(i); 21962b62e64dSKonrad Sztyber } 21972b62e64dSKonrad Sztyber } 21982b62e64dSKonrad Sztyber 219929784f35SKrzysztof Karas TAILQ_FOREACH(config, &job_config_list, link) { 220029784f35SKrzysztof Karas filenames = config->filename; 220129784f35SKrzysztof Karas 22028e373044SShuhei Matsumoto if (!g_one_thread_per_lcore) { 220329784f35SKrzysztof Karas thread = construct_job_thread(&config->cpumask, config->name); 22048e373044SShuhei Matsumoto } else { 22058e373044SShuhei Matsumoto thread = get_lcore_thread(config->lcore); 22068e373044SShuhei Matsumoto } 220729784f35SKrzysztof Karas assert(thread); 220829784f35SKrzysztof Karas 220929784f35SKrzysztof Karas while (filenames) { 221029784f35SKrzysztof Karas filenames = config_filename_next(filenames, filename); 221129784f35SKrzysztof Karas if (strlen(filename) == 0) { 221229784f35SKrzysztof Karas break; 221329784f35SKrzysztof Karas } 221429784f35SKrzysztof Karas 221529784f35SKrzysztof Karas bdev = spdk_bdev_get_by_name(filename); 221629784f35SKrzysztof Karas if (!bdev) { 221729784f35SKrzysztof Karas fprintf(stderr, "Unable to find bdev '%s'\n", filename); 221829784f35SKrzysztof Karas g_run_rc = -EINVAL; 221929784f35SKrzysztof Karas return; 222029784f35SKrzysztof Karas } 222129784f35SKrzysztof Karas 222229784f35SKrzysztof Karas rc = bdevperf_construct_job(bdev, config, thread); 222329784f35SKrzysztof Karas if (rc < 0) { 222429784f35SKrzysztof Karas g_run_rc = rc; 222529784f35SKrzysztof Karas return; 222629784f35SKrzysztof Karas } 222729784f35SKrzysztof Karas } 222829784f35SKrzysztof Karas } 222929784f35SKrzysztof Karas } 223029784f35SKrzysztof Karas 223129784f35SKrzysztof Karas static int 223229784f35SKrzysztof Karas make_cli_job_config(const char *filename, int64_t offset, uint64_t range) 223329784f35SKrzysztof Karas { 223429784f35SKrzysztof Karas struct job_config *config = calloc(1, sizeof(*config)); 223529784f35SKrzysztof Karas 223629784f35SKrzysztof Karas if (config == NULL) { 223729784f35SKrzysztof Karas fprintf(stderr, "Unable to allocate memory for job config\n"); 223829784f35SKrzysztof Karas return -ENOMEM; 223929784f35SKrzysztof Karas } 224029784f35SKrzysztof Karas 224129784f35SKrzysztof Karas config->name = filename; 224229784f35SKrzysztof Karas config->filename = filename; 22438e373044SShuhei Matsumoto config->lcore = _get_next_core(); 224429784f35SKrzysztof Karas spdk_cpuset_zero(&config->cpumask); 22458e373044SShuhei Matsumoto spdk_cpuset_set_cpu(&config->cpumask, config->lcore, true); 224629784f35SKrzysztof Karas config->bs = g_io_size; 224729784f35SKrzysztof Karas config->iodepth = g_queue_depth; 224829784f35SKrzysztof Karas config->rwmixread = g_rw_percentage; 224929784f35SKrzysztof Karas config->offset = offset; 225029784f35SKrzysztof Karas config->length = range; 225129784f35SKrzysztof Karas config->rw = parse_rw(g_workload_type, BDEVPERF_CONFIG_ERROR); 225229784f35SKrzysztof Karas if ((int)config->rw == BDEVPERF_CONFIG_ERROR) { 2253ef9168dcSGangCao free(config); 225429784f35SKrzysztof Karas return -EINVAL; 225529784f35SKrzysztof Karas } 225629784f35SKrzysztof Karas 225729784f35SKrzysztof Karas TAILQ_INSERT_TAIL(&job_config_list, config, link); 225829784f35SKrzysztof Karas return 0; 225929784f35SKrzysztof Karas } 226029784f35SKrzysztof Karas 22619ef9d4bdSShuhei Matsumoto static int 226256479486SShuhei Matsumoto bdevperf_construct_multithread_job_config(void *ctx, struct spdk_bdev *bdev) 22639ef9d4bdSShuhei Matsumoto { 226456479486SShuhei Matsumoto uint32_t *num_cores = ctx; 22659ef9d4bdSShuhei Matsumoto uint32_t i; 22669ef9d4bdSShuhei Matsumoto uint64_t blocks_per_job; 22679ef9d4bdSShuhei Matsumoto int64_t offset; 22689ef9d4bdSShuhei Matsumoto int rc; 22699ef9d4bdSShuhei Matsumoto 227056479486SShuhei Matsumoto blocks_per_job = spdk_bdev_get_num_blocks(bdev) / *num_cores; 22719ef9d4bdSShuhei Matsumoto offset = 0; 22729ef9d4bdSShuhei Matsumoto 22739ef9d4bdSShuhei Matsumoto SPDK_ENV_FOREACH_CORE(i) { 22749ef9d4bdSShuhei Matsumoto rc = make_cli_job_config(spdk_bdev_get_name(bdev), offset, blocks_per_job); 22759ef9d4bdSShuhei Matsumoto if (rc) { 22769ef9d4bdSShuhei Matsumoto return rc; 22779ef9d4bdSShuhei Matsumoto } 22789ef9d4bdSShuhei Matsumoto 22799ef9d4bdSShuhei Matsumoto offset += blocks_per_job; 22809ef9d4bdSShuhei Matsumoto } 22819ef9d4bdSShuhei Matsumoto 22829ef9d4bdSShuhei Matsumoto return 0; 22839ef9d4bdSShuhei Matsumoto } 22849ef9d4bdSShuhei Matsumoto 228529784f35SKrzysztof Karas static void 228629784f35SKrzysztof Karas bdevperf_construct_multithread_job_configs(void) 228729784f35SKrzysztof Karas { 228829784f35SKrzysztof Karas struct spdk_bdev *bdev; 228929784f35SKrzysztof Karas uint32_t i; 229029784f35SKrzysztof Karas uint32_t num_cores; 229129784f35SKrzysztof Karas 229229784f35SKrzysztof Karas num_cores = 0; 229329784f35SKrzysztof Karas SPDK_ENV_FOREACH_CORE(i) { 229429784f35SKrzysztof Karas num_cores++; 229529784f35SKrzysztof Karas } 229629784f35SKrzysztof Karas 229729784f35SKrzysztof Karas if (num_cores == 0) { 229829784f35SKrzysztof Karas g_run_rc = -EINVAL; 229929784f35SKrzysztof Karas return; 230029784f35SKrzysztof Karas } 230129784f35SKrzysztof Karas 230229784f35SKrzysztof Karas if (g_job_bdev_name != NULL) { 230329784f35SKrzysztof Karas bdev = spdk_bdev_get_by_name(g_job_bdev_name); 230429784f35SKrzysztof Karas if (!bdev) { 230529784f35SKrzysztof Karas fprintf(stderr, "Unable to find bdev '%s'\n", g_job_bdev_name); 230629784f35SKrzysztof Karas return; 230729784f35SKrzysztof Karas } 230856479486SShuhei Matsumoto g_run_rc = bdevperf_construct_multithread_job_config(&num_cores, bdev); 230929784f35SKrzysztof Karas } else { 231056479486SShuhei Matsumoto g_run_rc = spdk_for_each_bdev_leaf(&num_cores, bdevperf_construct_multithread_job_config); 231129784f35SKrzysztof Karas } 231256479486SShuhei Matsumoto 231329784f35SKrzysztof Karas } 231456479486SShuhei Matsumoto 231556479486SShuhei Matsumoto static int 231656479486SShuhei Matsumoto bdevperf_construct_job_config(void *ctx, struct spdk_bdev *bdev) 231756479486SShuhei Matsumoto { 231856479486SShuhei Matsumoto /* Construct the job */ 231956479486SShuhei Matsumoto return make_cli_job_config(spdk_bdev_get_name(bdev), 0, 0); 232029784f35SKrzysztof Karas } 232129784f35SKrzysztof Karas 232229784f35SKrzysztof Karas static void 232329784f35SKrzysztof Karas bdevperf_construct_job_configs(void) 232429784f35SKrzysztof Karas { 232529784f35SKrzysztof Karas struct spdk_bdev *bdev; 232629784f35SKrzysztof Karas 232729784f35SKrzysztof Karas /* There are three different modes for allocating jobs. Standard mode 232829784f35SKrzysztof Karas * (the default) creates one spdk_thread per bdev and runs the I/O job there. 232929784f35SKrzysztof Karas * 233029784f35SKrzysztof Karas * The -C flag places bdevperf into "multithread" mode, meaning it creates 233129784f35SKrzysztof Karas * one spdk_thread per bdev PER CORE, and runs a copy of the job on each. 233229784f35SKrzysztof Karas * This runs multiple threads per bdev, effectively. 233329784f35SKrzysztof Karas * 233429784f35SKrzysztof Karas * The -j flag implies "FIO" mode which tries to mimic semantic of FIO jobs. 233529784f35SKrzysztof Karas * In "FIO" mode, threads are spawned per-job instead of per-bdev. 233629784f35SKrzysztof Karas * Each FIO job can be individually parameterized by filename, cpu mask, etc, 233729784f35SKrzysztof Karas * which is different from other modes in that they only support global options. 23388e373044SShuhei Matsumoto * 23398e373044SShuhei Matsumoto * Both for standard mode and "multithread" mode, if the -E flag is specified, 23408e373044SShuhei Matsumoto * it creates one spdk_thread PER CORE. On each core, one spdk_thread is shared by 23418e373044SShuhei Matsumoto * multiple jobs. 234229784f35SKrzysztof Karas */ 234329784f35SKrzysztof Karas 234429784f35SKrzysztof Karas if (g_bdevperf_conf) { 234529784f35SKrzysztof Karas goto end; 234629784f35SKrzysztof Karas } 234729784f35SKrzysztof Karas 23488e373044SShuhei Matsumoto if (g_multithread_mode) { 23498e373044SShuhei Matsumoto bdevperf_construct_multithread_job_configs(); 23508e373044SShuhei Matsumoto } else if (g_job_bdev_name != NULL) { 235129784f35SKrzysztof Karas bdev = spdk_bdev_get_by_name(g_job_bdev_name); 235229784f35SKrzysztof Karas if (bdev) { 235329784f35SKrzysztof Karas /* Construct the job */ 235429784f35SKrzysztof Karas g_run_rc = make_cli_job_config(g_job_bdev_name, 0, 0); 235529784f35SKrzysztof Karas } else { 235629784f35SKrzysztof Karas fprintf(stderr, "Unable to find bdev '%s'\n", g_job_bdev_name); 235729784f35SKrzysztof Karas } 235829784f35SKrzysztof Karas } else { 235956479486SShuhei Matsumoto g_run_rc = spdk_for_each_bdev_leaf(NULL, bdevperf_construct_job_config); 236029784f35SKrzysztof Karas } 236129784f35SKrzysztof Karas 236229784f35SKrzysztof Karas end: 236329784f35SKrzysztof Karas /* Increment initial construct_jobs count so that it will never reach 0 in the middle 236429784f35SKrzysztof Karas * of iteration. 236529784f35SKrzysztof Karas */ 236629784f35SKrzysztof Karas g_construct_job_count = 1; 236729784f35SKrzysztof Karas 236829784f35SKrzysztof Karas if (g_run_rc == 0) { 236929784f35SKrzysztof Karas bdevperf_construct_jobs(); 237029784f35SKrzysztof Karas } 237129784f35SKrzysztof Karas 237229784f35SKrzysztof Karas _bdevperf_construct_job_done(NULL); 237329784f35SKrzysztof Karas } 237429784f35SKrzysztof Karas 237529784f35SKrzysztof Karas static int 237629784f35SKrzysztof Karas parse_uint_option(struct spdk_conf_section *s, const char *name, int def) 237729784f35SKrzysztof Karas { 237829784f35SKrzysztof Karas const char *job_name; 237929784f35SKrzysztof Karas int tmp; 238029784f35SKrzysztof Karas 238129784f35SKrzysztof Karas tmp = spdk_conf_section_get_intval(s, name); 238229784f35SKrzysztof Karas if (tmp == -1) { 238329784f35SKrzysztof Karas /* Field was not found. Check default value 238429784f35SKrzysztof Karas * In [global] section it is ok to have undefined values 238529784f35SKrzysztof Karas * but for other sections it is not ok */ 238629784f35SKrzysztof Karas if (def == BDEVPERF_CONFIG_UNDEFINED) { 238729784f35SKrzysztof Karas job_name = spdk_conf_section_get_name(s); 238829784f35SKrzysztof Karas if (strcmp(job_name, "global") == 0) { 238929784f35SKrzysztof Karas return def; 239029784f35SKrzysztof Karas } 239129784f35SKrzysztof Karas 239229784f35SKrzysztof Karas fprintf(stderr, 239329784f35SKrzysztof Karas "Job '%s' has no '%s' assigned\n", 239429784f35SKrzysztof Karas job_name, name); 239529784f35SKrzysztof Karas return BDEVPERF_CONFIG_ERROR; 239629784f35SKrzysztof Karas } 239729784f35SKrzysztof Karas return def; 239829784f35SKrzysztof Karas } 239929784f35SKrzysztof Karas 240029784f35SKrzysztof Karas /* NOTE: get_intval returns nonnegative on success */ 240129784f35SKrzysztof Karas if (tmp < 0) { 240229784f35SKrzysztof Karas fprintf(stderr, "Job '%s' has bad '%s' value.\n", 240329784f35SKrzysztof Karas spdk_conf_section_get_name(s), name); 240429784f35SKrzysztof Karas return BDEVPERF_CONFIG_ERROR; 240529784f35SKrzysztof Karas } 240629784f35SKrzysztof Karas 240729784f35SKrzysztof Karas return tmp; 240829784f35SKrzysztof Karas } 240929784f35SKrzysztof Karas 241029784f35SKrzysztof Karas /* CLI arguments override parameters for global sections */ 241129784f35SKrzysztof Karas static void 241229784f35SKrzysztof Karas config_set_cli_args(struct job_config *config) 241329784f35SKrzysztof Karas { 241429784f35SKrzysztof Karas if (g_job_bdev_name) { 241529784f35SKrzysztof Karas config->filename = g_job_bdev_name; 241629784f35SKrzysztof Karas } 241729784f35SKrzysztof Karas if (g_io_size > 0) { 241829784f35SKrzysztof Karas config->bs = g_io_size; 241929784f35SKrzysztof Karas } 242029784f35SKrzysztof Karas if (g_queue_depth > 0) { 242129784f35SKrzysztof Karas config->iodepth = g_queue_depth; 242229784f35SKrzysztof Karas } 242329784f35SKrzysztof Karas if (g_rw_percentage > 0) { 242429784f35SKrzysztof Karas config->rwmixread = g_rw_percentage; 242529784f35SKrzysztof Karas } 242629784f35SKrzysztof Karas if (g_workload_type) { 242729784f35SKrzysztof Karas config->rw = parse_rw(g_workload_type, config->rw); 242829784f35SKrzysztof Karas } 242929784f35SKrzysztof Karas } 243029784f35SKrzysztof Karas 243129784f35SKrzysztof Karas static int 243229784f35SKrzysztof Karas read_job_config(void) 243329784f35SKrzysztof Karas { 243429784f35SKrzysztof Karas struct job_config global_default_config; 243529784f35SKrzysztof Karas struct job_config global_config; 243629784f35SKrzysztof Karas struct spdk_conf_section *s; 2437ba87571dSShuhei Matsumoto struct job_config *config = NULL; 243829784f35SKrzysztof Karas const char *cpumask; 243929784f35SKrzysztof Karas const char *rw; 244029784f35SKrzysztof Karas bool is_global; 244129784f35SKrzysztof Karas int n = 0; 244229784f35SKrzysztof Karas int val; 244329784f35SKrzysztof Karas 244429784f35SKrzysztof Karas if (g_bdevperf_conf_file == NULL) { 244529784f35SKrzysztof Karas return 0; 244629784f35SKrzysztof Karas } 244729784f35SKrzysztof Karas 244829784f35SKrzysztof Karas g_bdevperf_conf = spdk_conf_allocate(); 244929784f35SKrzysztof Karas if (g_bdevperf_conf == NULL) { 245029784f35SKrzysztof Karas fprintf(stderr, "Could not allocate job config structure\n"); 245129784f35SKrzysztof Karas return 1; 245229784f35SKrzysztof Karas } 245329784f35SKrzysztof Karas 245429784f35SKrzysztof Karas spdk_conf_disable_sections_merge(g_bdevperf_conf); 245529784f35SKrzysztof Karas if (spdk_conf_read(g_bdevperf_conf, g_bdevperf_conf_file)) { 245629784f35SKrzysztof Karas fprintf(stderr, "Invalid job config"); 245729784f35SKrzysztof Karas return 1; 245829784f35SKrzysztof Karas } 245929784f35SKrzysztof Karas 246029784f35SKrzysztof Karas /* Initialize global defaults */ 246129784f35SKrzysztof Karas global_default_config.filename = NULL; 246229784f35SKrzysztof Karas /* Zero mask is the same as g_all_cpuset 246329784f35SKrzysztof Karas * The g_all_cpuset is not initialized yet, 246429784f35SKrzysztof Karas * so use zero mask as the default instead */ 246529784f35SKrzysztof Karas spdk_cpuset_zero(&global_default_config.cpumask); 246629784f35SKrzysztof Karas global_default_config.bs = BDEVPERF_CONFIG_UNDEFINED; 246729784f35SKrzysztof Karas global_default_config.iodepth = BDEVPERF_CONFIG_UNDEFINED; 246829784f35SKrzysztof Karas /* bdevperf has no default for -M option but in FIO the default is 50 */ 246929784f35SKrzysztof Karas global_default_config.rwmixread = 50; 247029784f35SKrzysztof Karas global_default_config.offset = 0; 247129784f35SKrzysztof Karas /* length 0 means 100% */ 247229784f35SKrzysztof Karas global_default_config.length = 0; 247329784f35SKrzysztof Karas global_default_config.rw = BDEVPERF_CONFIG_UNDEFINED; 247429784f35SKrzysztof Karas config_set_cli_args(&global_default_config); 247529784f35SKrzysztof Karas 247629784f35SKrzysztof Karas if ((int)global_default_config.rw == BDEVPERF_CONFIG_ERROR) { 247729784f35SKrzysztof Karas return 1; 247829784f35SKrzysztof Karas } 247929784f35SKrzysztof Karas 248029784f35SKrzysztof Karas /* There is only a single instance of global job_config 248129784f35SKrzysztof Karas * We just reset its value when we encounter new [global] section */ 248229784f35SKrzysztof Karas global_config = global_default_config; 248329784f35SKrzysztof Karas 248429784f35SKrzysztof Karas for (s = spdk_conf_first_section(g_bdevperf_conf); 248529784f35SKrzysztof Karas s != NULL; 248629784f35SKrzysztof Karas s = spdk_conf_next_section(s)) { 248729784f35SKrzysztof Karas config = calloc(1, sizeof(*config)); 248829784f35SKrzysztof Karas if (config == NULL) { 248929784f35SKrzysztof Karas fprintf(stderr, "Unable to allocate memory for job config\n"); 249029784f35SKrzysztof Karas return 1; 249129784f35SKrzysztof Karas } 249229784f35SKrzysztof Karas 249329784f35SKrzysztof Karas config->name = spdk_conf_section_get_name(s); 249429784f35SKrzysztof Karas is_global = strcmp(config->name, "global") == 0; 249529784f35SKrzysztof Karas 249629784f35SKrzysztof Karas if (is_global) { 249729784f35SKrzysztof Karas global_config = global_default_config; 249829784f35SKrzysztof Karas } 249929784f35SKrzysztof Karas 250029784f35SKrzysztof Karas config->filename = spdk_conf_section_get_val(s, "filename"); 250129784f35SKrzysztof Karas if (config->filename == NULL) { 250229784f35SKrzysztof Karas config->filename = global_config.filename; 250329784f35SKrzysztof Karas } 250429784f35SKrzysztof Karas if (!is_global) { 250529784f35SKrzysztof Karas if (config->filename == NULL) { 250629784f35SKrzysztof Karas fprintf(stderr, "Job '%s' expects 'filename' parameter\n", config->name); 250729784f35SKrzysztof Karas goto error; 250829784f35SKrzysztof Karas } else if (strnlen(config->filename, BDEVPERF_CONFIG_MAX_FILENAME) 250929784f35SKrzysztof Karas >= BDEVPERF_CONFIG_MAX_FILENAME) { 251029784f35SKrzysztof Karas fprintf(stderr, 251129784f35SKrzysztof Karas "filename for '%s' job is too long. Max length is %d\n", 251229784f35SKrzysztof Karas config->name, BDEVPERF_CONFIG_MAX_FILENAME); 251329784f35SKrzysztof Karas goto error; 251429784f35SKrzysztof Karas } 251529784f35SKrzysztof Karas } 251629784f35SKrzysztof Karas 251729784f35SKrzysztof Karas cpumask = spdk_conf_section_get_val(s, "cpumask"); 251829784f35SKrzysztof Karas if (cpumask == NULL) { 251929784f35SKrzysztof Karas config->cpumask = global_config.cpumask; 252029784f35SKrzysztof Karas } else if (spdk_cpuset_parse(&config->cpumask, cpumask)) { 252129784f35SKrzysztof Karas fprintf(stderr, "Job '%s' has bad 'cpumask' value\n", config->name); 252229784f35SKrzysztof Karas goto error; 252329784f35SKrzysztof Karas } 252429784f35SKrzysztof Karas 252529784f35SKrzysztof Karas config->bs = parse_uint_option(s, "bs", global_config.bs); 252629784f35SKrzysztof Karas if (config->bs == BDEVPERF_CONFIG_ERROR) { 252729784f35SKrzysztof Karas goto error; 252829784f35SKrzysztof Karas } else if (config->bs == 0) { 252929784f35SKrzysztof Karas fprintf(stderr, "'bs' of job '%s' must be greater than 0\n", config->name); 253029784f35SKrzysztof Karas goto error; 253129784f35SKrzysztof Karas } 253229784f35SKrzysztof Karas 253329784f35SKrzysztof Karas config->iodepth = parse_uint_option(s, "iodepth", global_config.iodepth); 253429784f35SKrzysztof Karas if (config->iodepth == BDEVPERF_CONFIG_ERROR) { 253529784f35SKrzysztof Karas goto error; 253629784f35SKrzysztof Karas } else if (config->iodepth == 0) { 253729784f35SKrzysztof Karas fprintf(stderr, 253829784f35SKrzysztof Karas "'iodepth' of job '%s' must be greater than 0\n", 253929784f35SKrzysztof Karas config->name); 254029784f35SKrzysztof Karas goto error; 254129784f35SKrzysztof Karas } 254229784f35SKrzysztof Karas 254329784f35SKrzysztof Karas config->rwmixread = parse_uint_option(s, "rwmixread", global_config.rwmixread); 254429784f35SKrzysztof Karas if (config->rwmixread == BDEVPERF_CONFIG_ERROR) { 254529784f35SKrzysztof Karas goto error; 254629784f35SKrzysztof Karas } else if (config->rwmixread > 100) { 254729784f35SKrzysztof Karas fprintf(stderr, 254829784f35SKrzysztof Karas "'rwmixread' value of '%s' job is not in 0-100 range\n", 254929784f35SKrzysztof Karas config->name); 255029784f35SKrzysztof Karas goto error; 255129784f35SKrzysztof Karas } 255229784f35SKrzysztof Karas 255329784f35SKrzysztof Karas config->offset = parse_uint_option(s, "offset", global_config.offset); 255429784f35SKrzysztof Karas if (config->offset == BDEVPERF_CONFIG_ERROR) { 255529784f35SKrzysztof Karas goto error; 255629784f35SKrzysztof Karas } 255729784f35SKrzysztof Karas 255829784f35SKrzysztof Karas val = parse_uint_option(s, "length", global_config.length); 255929784f35SKrzysztof Karas if (val == BDEVPERF_CONFIG_ERROR) { 256029784f35SKrzysztof Karas goto error; 256129784f35SKrzysztof Karas } 256229784f35SKrzysztof Karas config->length = val; 256329784f35SKrzysztof Karas 256429784f35SKrzysztof Karas rw = spdk_conf_section_get_val(s, "rw"); 256529784f35SKrzysztof Karas config->rw = parse_rw(rw, global_config.rw); 256629784f35SKrzysztof Karas if ((int)config->rw == BDEVPERF_CONFIG_ERROR) { 256729784f35SKrzysztof Karas fprintf(stderr, "Job '%s' has bad 'rw' value\n", config->name); 256829784f35SKrzysztof Karas goto error; 256929784f35SKrzysztof Karas } else if (!is_global && (int)config->rw == BDEVPERF_CONFIG_UNDEFINED) { 257029784f35SKrzysztof Karas fprintf(stderr, "Job '%s' has no 'rw' assigned\n", config->name); 257129784f35SKrzysztof Karas goto error; 257229784f35SKrzysztof Karas } 257329784f35SKrzysztof Karas 257429784f35SKrzysztof Karas if (is_global) { 257529784f35SKrzysztof Karas config_set_cli_args(config); 257629784f35SKrzysztof Karas global_config = *config; 257729784f35SKrzysztof Karas free(config); 2578488da916SMarcin Spiewak config = NULL; 257929784f35SKrzysztof Karas } else { 258029784f35SKrzysztof Karas TAILQ_INSERT_TAIL(&job_config_list, config, link); 258129784f35SKrzysztof Karas n++; 258229784f35SKrzysztof Karas } 258329784f35SKrzysztof Karas } 258429784f35SKrzysztof Karas 2585ba87571dSShuhei Matsumoto if (g_rpc_log_file_name != NULL) { 2586ba87571dSShuhei Matsumoto g_rpc_log_file = fopen(g_rpc_log_file_name, "a"); 2587ba87571dSShuhei Matsumoto if (g_rpc_log_file == NULL) { 2588ba87571dSShuhei Matsumoto fprintf(stderr, "Failed to open %s\n", g_rpc_log_file_name); 2589ba87571dSShuhei Matsumoto goto error; 2590ba87571dSShuhei Matsumoto } 2591ba87571dSShuhei Matsumoto } 2592ba87571dSShuhei Matsumoto 259329784f35SKrzysztof Karas printf("Using job config with %d jobs\n", n); 259429784f35SKrzysztof Karas return 0; 259529784f35SKrzysztof Karas error: 259629784f35SKrzysztof Karas free(config); 259729784f35SKrzysztof Karas return 1; 259829784f35SKrzysztof Karas } 259929784f35SKrzysztof Karas 260029784f35SKrzysztof Karas static void 260129784f35SKrzysztof Karas bdevperf_run(void *arg1) 260229784f35SKrzysztof Karas { 260329784f35SKrzysztof Karas uint32_t i; 260429784f35SKrzysztof Karas 260529784f35SKrzysztof Karas g_main_thread = spdk_get_thread(); 260629784f35SKrzysztof Karas 260729784f35SKrzysztof Karas spdk_cpuset_zero(&g_all_cpuset); 260829784f35SKrzysztof Karas SPDK_ENV_FOREACH_CORE(i) { 260929784f35SKrzysztof Karas spdk_cpuset_set_cpu(&g_all_cpuset, i, true); 261029784f35SKrzysztof Karas } 261129784f35SKrzysztof Karas 261229784f35SKrzysztof Karas if (g_wait_for_tests) { 261329784f35SKrzysztof Karas /* Do not perform any tests until RPC is received */ 261429784f35SKrzysztof Karas return; 261529784f35SKrzysztof Karas } 261629784f35SKrzysztof Karas 261729784f35SKrzysztof Karas bdevperf_construct_job_configs(); 261829784f35SKrzysztof Karas } 261929784f35SKrzysztof Karas 262029784f35SKrzysztof Karas static void 2621ed4b89aaSGangCao rpc_perform_tests_reset(void) 2622ed4b89aaSGangCao { 2623ed4b89aaSGangCao /* Reset g_run_rc to 0 for the next test run. */ 2624ed4b89aaSGangCao g_run_rc = 0; 2625ed4b89aaSGangCao 2626ed4b89aaSGangCao /* Reset g_stats to 0 for the next test run. */ 2627ed4b89aaSGangCao memset(&g_stats, 0, sizeof(g_stats)); 2628ed4b89aaSGangCao 2629ed4b89aaSGangCao /* Reset g_show_performance_period_num to 0 for the next test run. */ 2630ed4b89aaSGangCao g_show_performance_period_num = 0; 2631ed4b89aaSGangCao } 2632ed4b89aaSGangCao 2633ed4b89aaSGangCao static void 263429784f35SKrzysztof Karas rpc_perform_tests_cb(void) 263529784f35SKrzysztof Karas { 263629784f35SKrzysztof Karas struct spdk_jsonrpc_request *request = g_request; 263729784f35SKrzysztof Karas 263829784f35SKrzysztof Karas g_request = NULL; 263929784f35SKrzysztof Karas 26403a141b72SMarcin Spiewak if (g_run_rc) { 264129784f35SKrzysztof Karas spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, 264229784f35SKrzysztof Karas "bdevperf failed with error %s", spdk_strerror(-g_run_rc)); 264329784f35SKrzysztof Karas } 264429784f35SKrzysztof Karas 2645ed4b89aaSGangCao rpc_perform_tests_reset(); 264629784f35SKrzysztof Karas } 264729784f35SKrzysztof Karas 2648bbf8dfb7SEugene Kobyak struct rpc_bdevperf_params { 2649bbf8dfb7SEugene Kobyak int time_in_sec; 2650bbf8dfb7SEugene Kobyak char *workload_type; 2651bbf8dfb7SEugene Kobyak int queue_depth; 2652bbf8dfb7SEugene Kobyak char *io_size; 2653bbf8dfb7SEugene Kobyak int rw_percentage; 2654bbf8dfb7SEugene Kobyak }; 2655bbf8dfb7SEugene Kobyak 2656bbf8dfb7SEugene Kobyak static const struct spdk_json_object_decoder rpc_bdevperf_params_decoders[] = { 2657bbf8dfb7SEugene Kobyak {"time_in_sec", offsetof(struct rpc_bdevperf_params, time_in_sec), spdk_json_decode_int32, true}, 2658bbf8dfb7SEugene Kobyak {"workload_type", offsetof(struct rpc_bdevperf_params, workload_type), spdk_json_decode_string, true}, 2659bbf8dfb7SEugene Kobyak {"queue_depth", offsetof(struct rpc_bdevperf_params, queue_depth), spdk_json_decode_int32, true}, 2660bbf8dfb7SEugene Kobyak {"io_size", offsetof(struct rpc_bdevperf_params, io_size), spdk_json_decode_string, true}, 2661bbf8dfb7SEugene Kobyak {"rw_percentage", offsetof(struct rpc_bdevperf_params, rw_percentage), spdk_json_decode_int32, true}, 2662bbf8dfb7SEugene Kobyak }; 2663bbf8dfb7SEugene Kobyak 2664bbf8dfb7SEugene Kobyak static void 2665bbf8dfb7SEugene Kobyak rpc_apply_bdevperf_params(struct rpc_bdevperf_params *params) 2666bbf8dfb7SEugene Kobyak { 2667bbf8dfb7SEugene Kobyak if (params->workload_type) { 2668bbf8dfb7SEugene Kobyak /* we need to clear previously settled parameter to avoid memory leak */ 2669bbf8dfb7SEugene Kobyak free(g_workload_type); 2670bbf8dfb7SEugene Kobyak g_workload_type = strdup(params->workload_type); 2671bbf8dfb7SEugene Kobyak } 2672bbf8dfb7SEugene Kobyak if (params->queue_depth) { 2673bbf8dfb7SEugene Kobyak g_queue_depth = params->queue_depth; 2674bbf8dfb7SEugene Kobyak } 2675bbf8dfb7SEugene Kobyak if (params->io_size) { 2676bbf8dfb7SEugene Kobyak bdevperf_parse_arg('o', params->io_size); 2677bbf8dfb7SEugene Kobyak } 2678bbf8dfb7SEugene Kobyak if (params->time_in_sec) { 2679bbf8dfb7SEugene Kobyak g_time_in_sec = params->time_in_sec; 2680bbf8dfb7SEugene Kobyak } 2681bbf8dfb7SEugene Kobyak if (params->rw_percentage) { 2682bbf8dfb7SEugene Kobyak g_rw_percentage = params->rw_percentage; 2683bbf8dfb7SEugene Kobyak g_mix_specified = true; 2684bbf8dfb7SEugene Kobyak } else { 2685bbf8dfb7SEugene Kobyak g_mix_specified = false; 2686bbf8dfb7SEugene Kobyak } 2687bbf8dfb7SEugene Kobyak } 2688bbf8dfb7SEugene Kobyak 268929784f35SKrzysztof Karas static void 269029784f35SKrzysztof Karas rpc_perform_tests(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) 269129784f35SKrzysztof Karas { 2692bbf8dfb7SEugene Kobyak struct rpc_bdevperf_params req = {}, backup = {}; 2693bbf8dfb7SEugene Kobyak int rc; 2694bbf8dfb7SEugene Kobyak 269529784f35SKrzysztof Karas if (g_request != NULL) { 269629784f35SKrzysztof Karas fprintf(stderr, "Another test is already in progress.\n"); 269729784f35SKrzysztof Karas spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, 269829784f35SKrzysztof Karas spdk_strerror(-EINPROGRESS)); 269929784f35SKrzysztof Karas return; 270029784f35SKrzysztof Karas } 2701bbf8dfb7SEugene Kobyak 2702bbf8dfb7SEugene Kobyak if (params) { 2703bbf8dfb7SEugene Kobyak if (spdk_json_decode_object_relaxed(params, rpc_bdevperf_params_decoders, 2704bbf8dfb7SEugene Kobyak SPDK_COUNTOF(rpc_bdevperf_params_decoders), 2705bbf8dfb7SEugene Kobyak &req)) { 2706bbf8dfb7SEugene Kobyak spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_PARSE_ERROR, 2707bbf8dfb7SEugene Kobyak "spdk_json_decode_object failed"); 2708bbf8dfb7SEugene Kobyak return; 2709bbf8dfb7SEugene Kobyak } 2710bbf8dfb7SEugene Kobyak 2711bbf8dfb7SEugene Kobyak if (g_workload_type) { 2712bbf8dfb7SEugene Kobyak backup.workload_type = strdup(g_workload_type); 2713bbf8dfb7SEugene Kobyak } 2714bbf8dfb7SEugene Kobyak backup.queue_depth = g_queue_depth; 2715bbf8dfb7SEugene Kobyak if (asprintf(&backup.io_size, "%d", g_io_size) < 0) { 2716bbf8dfb7SEugene Kobyak fprintf(stderr, "Couldn't allocate memory for queue depth"); 2717bbf8dfb7SEugene Kobyak goto rpc_error; 2718bbf8dfb7SEugene Kobyak } 2719bbf8dfb7SEugene Kobyak backup.time_in_sec = g_time_in_sec; 2720bbf8dfb7SEugene Kobyak backup.rw_percentage = g_rw_percentage; 2721bbf8dfb7SEugene Kobyak 2722bbf8dfb7SEugene Kobyak rpc_apply_bdevperf_params(&req); 2723bbf8dfb7SEugene Kobyak 2724bbf8dfb7SEugene Kobyak free(req.workload_type); 2725bbf8dfb7SEugene Kobyak free(req.io_size); 2726bbf8dfb7SEugene Kobyak } 2727bbf8dfb7SEugene Kobyak 2728bbf8dfb7SEugene Kobyak rc = verify_test_params(); 2729bbf8dfb7SEugene Kobyak 2730bbf8dfb7SEugene Kobyak if (rc) { 2731bbf8dfb7SEugene Kobyak spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_PARSE_ERROR, 2732bbf8dfb7SEugene Kobyak "Invalid parameters provided"); 2733bbf8dfb7SEugene Kobyak /* restore old params on error */ 2734bbf8dfb7SEugene Kobyak rpc_apply_bdevperf_params(&backup); 2735bbf8dfb7SEugene Kobyak goto rpc_error; 2736bbf8dfb7SEugene Kobyak } 2737bbf8dfb7SEugene Kobyak 273829784f35SKrzysztof Karas g_request = request; 273929784f35SKrzysztof Karas 274029784f35SKrzysztof Karas /* Only construct job configs at the first test run. */ 274129784f35SKrzysztof Karas if (TAILQ_EMPTY(&job_config_list)) { 274229784f35SKrzysztof Karas bdevperf_construct_job_configs(); 274329784f35SKrzysztof Karas } else { 274429784f35SKrzysztof Karas bdevperf_construct_jobs(); 274529784f35SKrzysztof Karas } 2746bbf8dfb7SEugene Kobyak 2747bbf8dfb7SEugene Kobyak rpc_error: 2748bbf8dfb7SEugene Kobyak free(backup.io_size); 2749bbf8dfb7SEugene Kobyak free(backup.workload_type); 275029784f35SKrzysztof Karas } 275129784f35SKrzysztof Karas SPDK_RPC_REGISTER("perform_tests", rpc_perform_tests, SPDK_RPC_RUNTIME) 275229784f35SKrzysztof Karas 275329784f35SKrzysztof Karas static void 275429784f35SKrzysztof Karas spdk_bdevperf_shutdown_cb(void) 275529784f35SKrzysztof Karas { 275629784f35SKrzysztof Karas g_shutdown = true; 275729784f35SKrzysztof Karas struct bdevperf_job *job, *tmp; 275829784f35SKrzysztof Karas 275929784f35SKrzysztof Karas if (g_bdevperf.running_jobs == 0) { 276029784f35SKrzysztof Karas bdevperf_test_done(NULL); 276129784f35SKrzysztof Karas return; 276229784f35SKrzysztof Karas } 276329784f35SKrzysztof Karas 276429784f35SKrzysztof Karas /* Iterate jobs to stop all I/O */ 276529784f35SKrzysztof Karas TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, tmp) { 276629784f35SKrzysztof Karas spdk_thread_send_msg(job->thread, _bdevperf_job_drain, job); 276729784f35SKrzysztof Karas } 276829784f35SKrzysztof Karas } 276929784f35SKrzysztof Karas 277029784f35SKrzysztof Karas static int 277129784f35SKrzysztof Karas bdevperf_parse_arg(int ch, char *arg) 277229784f35SKrzysztof Karas { 277329784f35SKrzysztof Karas long long tmp; 277429784f35SKrzysztof Karas 277529784f35SKrzysztof Karas if (ch == 'w') { 2776d1215940SEugene Kobyak g_workload_type = strdup(arg); 277729784f35SKrzysztof Karas } else if (ch == 'T') { 27788dcc37abSEugene Kobyak g_job_bdev_name = arg; 277929784f35SKrzysztof Karas } else if (ch == 'z') { 278029784f35SKrzysztof Karas g_wait_for_tests = true; 278129784f35SKrzysztof Karas } else if (ch == 'Z') { 278229784f35SKrzysztof Karas g_zcopy = true; 278329784f35SKrzysztof Karas } else if (ch == 'X') { 278429784f35SKrzysztof Karas g_abort = true; 278529784f35SKrzysztof Karas } else if (ch == 'C') { 278629784f35SKrzysztof Karas g_multithread_mode = true; 278729784f35SKrzysztof Karas } else if (ch == 'f') { 278829784f35SKrzysztof Karas g_continue_on_failure = true; 278929784f35SKrzysztof Karas } else if (ch == 'j') { 27908dcc37abSEugene Kobyak g_bdevperf_conf_file = arg; 279129784f35SKrzysztof Karas } else if (ch == 'F') { 279229784f35SKrzysztof Karas char *endptr; 279329784f35SKrzysztof Karas 279429784f35SKrzysztof Karas errno = 0; 27958dcc37abSEugene Kobyak g_zipf_theta = strtod(arg, &endptr); 27968dcc37abSEugene Kobyak if (errno || arg == endptr || g_zipf_theta < 0) { 27978dcc37abSEugene Kobyak fprintf(stderr, "Illegal zipf theta value %s\n", arg); 279829784f35SKrzysztof Karas return -EINVAL; 279929784f35SKrzysztof Karas } 2800723dd06eSRichael Zhuang } else if (ch == 'l') { 2801723dd06eSRichael Zhuang g_latency_display_level++; 2802d254a3b9SSlawomir Ptak } else if (ch == 'D') { 2803d254a3b9SSlawomir Ptak g_random_map = true; 28048e373044SShuhei Matsumoto } else if (ch == 'E') { 28058e373044SShuhei Matsumoto g_one_thread_per_lcore = true; 2806ba87571dSShuhei Matsumoto } else if (ch == 'J') { 28078dcc37abSEugene Kobyak g_rpc_log_file_name = arg; 280891c770a2SKonrad Sztyber } else if (ch == 'o') { 280991c770a2SKonrad Sztyber uint64_t size; 281091c770a2SKonrad Sztyber 28118dcc37abSEugene Kobyak if (spdk_parse_capacity(arg, &size, NULL) != 0) { 28128dcc37abSEugene Kobyak fprintf(stderr, "Invalid IO size: %s\n", arg); 281391c770a2SKonrad Sztyber return -EINVAL; 281491c770a2SKonrad Sztyber } 281591c770a2SKonrad Sztyber g_io_size = (int)size; 28163353236fSArtur Paszkiewicz } else if (ch == 'U') { 28173353236fSArtur Paszkiewicz g_unique_writes = true; 2818*971ec012SShuhei Matsumoto } else if (ch == 'N') { 2819*971ec012SShuhei Matsumoto g_hide_metadata = true; 282029784f35SKrzysztof Karas } else { 28218dcc37abSEugene Kobyak tmp = spdk_strtoll(arg, 10); 282229784f35SKrzysztof Karas if (tmp < 0) { 282329784f35SKrzysztof Karas fprintf(stderr, "Parse failed for the option %c.\n", ch); 282429784f35SKrzysztof Karas return tmp; 282529784f35SKrzysztof Karas } else if (tmp >= INT_MAX) { 282629784f35SKrzysztof Karas fprintf(stderr, "Parsed option was too large %c.\n", ch); 282729784f35SKrzysztof Karas return -ERANGE; 282829784f35SKrzysztof Karas } 282929784f35SKrzysztof Karas 283029784f35SKrzysztof Karas switch (ch) { 283129784f35SKrzysztof Karas case 'q': 283229784f35SKrzysztof Karas g_queue_depth = tmp; 283329784f35SKrzysztof Karas break; 283429784f35SKrzysztof Karas case 't': 283529784f35SKrzysztof Karas g_time_in_sec = tmp; 283629784f35SKrzysztof Karas break; 283729784f35SKrzysztof Karas case 'k': 283829784f35SKrzysztof Karas g_timeout_in_sec = tmp; 283929784f35SKrzysztof Karas break; 284029784f35SKrzysztof Karas case 'M': 284129784f35SKrzysztof Karas g_rw_percentage = tmp; 284229784f35SKrzysztof Karas g_mix_specified = true; 284329784f35SKrzysztof Karas break; 284429784f35SKrzysztof Karas case 'P': 284529784f35SKrzysztof Karas g_show_performance_ema_period = tmp; 284629784f35SKrzysztof Karas break; 284729784f35SKrzysztof Karas case 'S': 284827134348SAnisa Su g_summarize_performance = false; 2849045c781dSRichael Zhuang g_show_performance_period_in_usec = tmp * SPDK_SEC_TO_USEC; 285029784f35SKrzysztof Karas break; 285129784f35SKrzysztof Karas default: 285229784f35SKrzysztof Karas return -EINVAL; 285329784f35SKrzysztof Karas } 285429784f35SKrzysztof Karas } 285529784f35SKrzysztof Karas return 0; 285629784f35SKrzysztof Karas } 285729784f35SKrzysztof Karas 285829784f35SKrzysztof Karas static void 285929784f35SKrzysztof Karas bdevperf_usage(void) 286029784f35SKrzysztof Karas { 286129784f35SKrzysztof Karas printf(" -q <depth> io depth\n"); 286229784f35SKrzysztof Karas printf(" -o <size> io size in bytes\n"); 286332e5ce31SAmir Haroush printf(" -w <type> io pattern type, must be one of " PATTERN_TYPES_STR "\n"); 286429784f35SKrzysztof Karas printf(" -t <time> time in seconds\n"); 286529784f35SKrzysztof Karas printf(" -k <timeout> timeout in seconds to detect starved I/O (default is 0 and disabled)\n"); 286629784f35SKrzysztof Karas printf(" -M <percent> rwmixread (100 for reads, 0 for writes)\n"); 286729784f35SKrzysztof Karas printf(" -P <num> number of moving average period\n"); 286829784f35SKrzysztof Karas printf("\t\t(If set to n, show weighted mean of the previous n IO/s in real time)\n"); 286929784f35SKrzysztof Karas printf("\t\t(Formula: M = 2 / (n + 1), EMA[i+1] = IO/s * M + (1 - M) * EMA[i])\n"); 287029784f35SKrzysztof Karas printf("\t\t(only valid with -S)\n"); 287129784f35SKrzysztof Karas printf(" -S <period> show performance result in real time every <period> seconds\n"); 287229784f35SKrzysztof Karas printf(" -T <bdev> bdev to run against. Default: all available bdevs.\n"); 287329784f35SKrzysztof Karas printf(" -f continue processing I/O even after failures\n"); 287429784f35SKrzysztof Karas printf(" -F <zipf theta> use zipf distribution for random I/O\n"); 287529784f35SKrzysztof Karas printf(" -Z enable using zcopy bdev API for read or write I/O\n"); 2876eb1735faSJim Harris printf(" -z start bdevperf, but wait for perform_tests RPC to start tests\n"); 2877eb1735faSJim Harris printf(" (See examples/bdev/bdevperf/bdevperf.py)\n"); 287829784f35SKrzysztof Karas printf(" -X abort timed out I/O\n"); 287929784f35SKrzysztof Karas printf(" -C enable every core to send I/Os to each bdev\n"); 288029784f35SKrzysztof Karas printf(" -j <filename> use job config file\n"); 2881723dd06eSRichael Zhuang printf(" -l display latency histogram, default: disable. -l display summary, -ll display details\n"); 2882d254a3b9SSlawomir Ptak printf(" -D use a random map for picking offsets not previously read or written (for all jobs)\n"); 28838e373044SShuhei Matsumoto printf(" -E share per lcore thread among jobs. Available only if -j is not used.\n"); 2884ba87571dSShuhei Matsumoto printf(" -J File name to open with append mode and log JSON RPC calls.\n"); 28853353236fSArtur Paszkiewicz printf(" -U generate unique data for each write I/O, has no effect on non-write I/O\n"); 2886*971ec012SShuhei Matsumoto printf(" -N Enable hide_metadata option to each bdev\n"); 288729784f35SKrzysztof Karas } 288829784f35SKrzysztof Karas 2889a1f4f11bSShuhei Matsumoto static void 2890a1f4f11bSShuhei Matsumoto bdevperf_fini(void) 2891a1f4f11bSShuhei Matsumoto { 2892a1f4f11bSShuhei Matsumoto free_job_config(); 2893d1215940SEugene Kobyak free(g_workload_type); 2894ba87571dSShuhei Matsumoto 2895ba87571dSShuhei Matsumoto if (g_rpc_log_file != NULL) { 2896ba87571dSShuhei Matsumoto fclose(g_rpc_log_file); 2897ba87571dSShuhei Matsumoto g_rpc_log_file = NULL; 2898ba87571dSShuhei Matsumoto } 2899a1f4f11bSShuhei Matsumoto } 2900a1f4f11bSShuhei Matsumoto 290129784f35SKrzysztof Karas static int 2902d1215940SEugene Kobyak verify_test_params(void) 290329784f35SKrzysztof Karas { 290429784f35SKrzysztof Karas if (!g_bdevperf_conf_file && g_queue_depth <= 0) { 290529784f35SKrzysztof Karas goto out; 290629784f35SKrzysztof Karas } 290729784f35SKrzysztof Karas if (!g_bdevperf_conf_file && g_io_size <= 0) { 290829784f35SKrzysztof Karas goto out; 290929784f35SKrzysztof Karas } 291029784f35SKrzysztof Karas if (!g_bdevperf_conf_file && !g_workload_type) { 291129784f35SKrzysztof Karas goto out; 291229784f35SKrzysztof Karas } 29138e373044SShuhei Matsumoto if (g_bdevperf_conf_file && g_one_thread_per_lcore) { 29148e373044SShuhei Matsumoto printf("If bdevperf's config file is used, per lcore thread cannot be used\n"); 29158e373044SShuhei Matsumoto goto out; 29168e373044SShuhei Matsumoto } 291729784f35SKrzysztof Karas if (g_time_in_sec <= 0) { 291829784f35SKrzysztof Karas goto out; 291929784f35SKrzysztof Karas } 2920045c781dSRichael Zhuang g_time_in_usec = g_time_in_sec * SPDK_SEC_TO_USEC; 292129784f35SKrzysztof Karas 292229784f35SKrzysztof Karas if (g_timeout_in_sec < 0) { 292329784f35SKrzysztof Karas goto out; 292429784f35SKrzysztof Karas } 292529784f35SKrzysztof Karas 292629784f35SKrzysztof Karas if (g_abort && !g_timeout_in_sec) { 292729784f35SKrzysztof Karas printf("Timeout must be set for abort option, Ignoring g_abort\n"); 292829784f35SKrzysztof Karas } 292929784f35SKrzysztof Karas 293027134348SAnisa Su if (g_show_performance_ema_period > 0 && g_summarize_performance) { 293129784f35SKrzysztof Karas fprintf(stderr, "-P option must be specified with -S option\n"); 293229784f35SKrzysztof Karas return 1; 293329784f35SKrzysztof Karas } 293429784f35SKrzysztof Karas 293529784f35SKrzysztof Karas if (g_io_size > SPDK_BDEV_LARGE_BUF_MAX_SIZE) { 293629784f35SKrzysztof Karas printf("I/O size of %d is greater than zero copy threshold (%d).\n", 293729784f35SKrzysztof Karas g_io_size, SPDK_BDEV_LARGE_BUF_MAX_SIZE); 293829784f35SKrzysztof Karas printf("Zero copy mechanism will not be used.\n"); 293929784f35SKrzysztof Karas g_zcopy = false; 294029784f35SKrzysztof Karas } 294129784f35SKrzysztof Karas 294229784f35SKrzysztof Karas if (g_bdevperf_conf_file) { 294329784f35SKrzysztof Karas /* workload_type verification happens during config file parsing */ 294429784f35SKrzysztof Karas return 0; 294529784f35SKrzysztof Karas } 294629784f35SKrzysztof Karas 294729784f35SKrzysztof Karas if (!strcmp(g_workload_type, "verify") || 294829784f35SKrzysztof Karas !strcmp(g_workload_type, "reset")) { 294929784f35SKrzysztof Karas g_rw_percentage = 50; 295029784f35SKrzysztof Karas g_verify = true; 295129784f35SKrzysztof Karas if (!strcmp(g_workload_type, "reset")) { 295229784f35SKrzysztof Karas g_reset = true; 295329784f35SKrzysztof Karas } 295429784f35SKrzysztof Karas } 295529784f35SKrzysztof Karas 295629784f35SKrzysztof Karas if (!strcmp(g_workload_type, "read") || 295729784f35SKrzysztof Karas !strcmp(g_workload_type, "randread") || 295829784f35SKrzysztof Karas !strcmp(g_workload_type, "write") || 295929784f35SKrzysztof Karas !strcmp(g_workload_type, "randwrite") || 296029784f35SKrzysztof Karas !strcmp(g_workload_type, "verify") || 296129784f35SKrzysztof Karas !strcmp(g_workload_type, "reset") || 296229784f35SKrzysztof Karas !strcmp(g_workload_type, "unmap") || 296329784f35SKrzysztof Karas !strcmp(g_workload_type, "write_zeroes") || 296429784f35SKrzysztof Karas !strcmp(g_workload_type, "flush")) { 296529784f35SKrzysztof Karas if (g_mix_specified) { 296629784f35SKrzysztof Karas fprintf(stderr, "Ignoring -M option... Please use -M option" 296729784f35SKrzysztof Karas " only when using rw or randrw.\n"); 296829784f35SKrzysztof Karas } 296929784f35SKrzysztof Karas } 297029784f35SKrzysztof Karas 297129784f35SKrzysztof Karas if (!strcmp(g_workload_type, "rw") || 297229784f35SKrzysztof Karas !strcmp(g_workload_type, "randrw")) { 297329784f35SKrzysztof Karas if (g_rw_percentage < 0 || g_rw_percentage > 100) { 297429784f35SKrzysztof Karas fprintf(stderr, 297529784f35SKrzysztof Karas "-M must be specified to value from 0 to 100 " 297629784f35SKrzysztof Karas "for rw or randrw.\n"); 297729784f35SKrzysztof Karas return 1; 297829784f35SKrzysztof Karas } 297929784f35SKrzysztof Karas } 298029784f35SKrzysztof Karas 2981d254a3b9SSlawomir Ptak if (strcmp(g_workload_type, "randread") && 2982d254a3b9SSlawomir Ptak strcmp(g_workload_type, "randwrite") && 2983d254a3b9SSlawomir Ptak strcmp(g_workload_type, "randrw")) { 2984d254a3b9SSlawomir Ptak if (g_random_map) { 2985d254a3b9SSlawomir Ptak fprintf(stderr, "Ignoring -D option... Please use -D option" 2986d254a3b9SSlawomir Ptak " only when using randread, randwrite or randrw.\n"); 2987d254a3b9SSlawomir Ptak return 1; 2988d254a3b9SSlawomir Ptak } 2989d254a3b9SSlawomir Ptak } 2990d254a3b9SSlawomir Ptak 299129784f35SKrzysztof Karas return 0; 299229784f35SKrzysztof Karas out: 299329784f35SKrzysztof Karas return 1; 299429784f35SKrzysztof Karas } 299529784f35SKrzysztof Karas 299629784f35SKrzysztof Karas int 299729784f35SKrzysztof Karas main(int argc, char **argv) 299829784f35SKrzysztof Karas { 299929784f35SKrzysztof Karas struct spdk_app_opts opts = {}; 300029784f35SKrzysztof Karas int rc; 300129784f35SKrzysztof Karas 300229784f35SKrzysztof Karas /* Use the runtime PID to set the random seed */ 300329784f35SKrzysztof Karas srand(getpid()); 300429784f35SKrzysztof Karas 300529784f35SKrzysztof Karas spdk_app_opts_init(&opts, sizeof(opts)); 300629784f35SKrzysztof Karas opts.name = "bdevperf"; 300729784f35SKrzysztof Karas opts.rpc_addr = NULL; 300829784f35SKrzysztof Karas opts.shutdown_cb = spdk_bdevperf_shutdown_cb; 300929784f35SKrzysztof Karas 3010*971ec012SShuhei Matsumoto if ((rc = spdk_app_parse_args(argc, argv, &opts, "Zzfq:o:t:w:k:CEF:J:M:P:S:T:Xlj:DUN", NULL, 301129784f35SKrzysztof Karas bdevperf_parse_arg, bdevperf_usage)) != 301229784f35SKrzysztof Karas SPDK_APP_PARSE_ARGS_SUCCESS) { 301329784f35SKrzysztof Karas return rc; 301429784f35SKrzysztof Karas } 301529784f35SKrzysztof Karas 3016d1215940SEugene Kobyak /* Set the default address if no rpc_addr was provided in args 3017d1215940SEugene Kobyak * and RPC is used for starting tests */ 3018d1215940SEugene Kobyak if (g_wait_for_tests && opts.rpc_addr == NULL) { 3019d1215940SEugene Kobyak opts.rpc_addr = SPDK_DEFAULT_RPC_ADDR; 3020d1215940SEugene Kobyak } 3021d1215940SEugene Kobyak 302229784f35SKrzysztof Karas if (read_job_config()) { 3023a1f4f11bSShuhei Matsumoto bdevperf_fini(); 302429784f35SKrzysztof Karas return 1; 302529784f35SKrzysztof Karas } 302629784f35SKrzysztof Karas 3027d1215940SEugene Kobyak if (g_rpc_log_file != NULL) { 3028d1215940SEugene Kobyak opts.rpc_log_file = g_rpc_log_file; 3029d1215940SEugene Kobyak } 3030d1215940SEugene Kobyak 3031bbf8dfb7SEugene Kobyak if (verify_test_params() != 0 && !g_wait_for_tests) { 3032d1215940SEugene Kobyak spdk_app_usage(); 3033d1215940SEugene Kobyak bdevperf_usage(); 3034a1f4f11bSShuhei Matsumoto bdevperf_fini(); 303529784f35SKrzysztof Karas exit(1); 303629784f35SKrzysztof Karas } 303729784f35SKrzysztof Karas 303829784f35SKrzysztof Karas rc = spdk_app_start(&opts, bdevperf_run, NULL); 303929784f35SKrzysztof Karas 304029784f35SKrzysztof Karas spdk_app_fini(); 3041a1f4f11bSShuhei Matsumoto bdevperf_fini(); 304229784f35SKrzysztof Karas return rc; 304329784f35SKrzysztof Karas } 3044