Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2016 Intel Corporation.
3 : : * Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES.
4 : : * All rights reserved.
5 : : */
6 : :
7 : : #include "spdk/stdinc.h"
8 : :
9 : : #include "spdk/bdev.h"
10 : : #include "spdk/accel.h"
11 : : #include "spdk/endian.h"
12 : : #include "spdk/env.h"
13 : : #include "spdk/event.h"
14 : : #include "spdk/log.h"
15 : : #include "spdk/util.h"
16 : : #include "spdk/thread.h"
17 : : #include "spdk/string.h"
18 : : #include "spdk/rpc.h"
19 : : #include "spdk/bit_array.h"
20 : : #include "spdk/conf.h"
21 : : #include "spdk/zipf.h"
22 : : #include "spdk/histogram_data.h"
23 : :
24 : : #define BDEVPERF_CONFIG_MAX_FILENAME 1024
25 : : #define BDEVPERF_CONFIG_UNDEFINED -1
26 : : #define BDEVPERF_CONFIG_ERROR -2
27 : : #define PATTERN_TYPES_STR "(read, write, randread, randwrite, rw, randrw, verify, reset, unmap, flush, write_zeroes)"
28 : : #define BDEVPERF_MAX_COREMASK_STRING 64
29 : :
30 : : struct bdevperf_task {
31 : : struct iovec iov;
32 : : struct bdevperf_job *job;
33 : : struct spdk_bdev_io *bdev_io;
34 : : void *buf;
35 : : void *verify_buf;
36 : : void *md_buf;
37 : : void *verify_md_buf;
38 : : uint64_t offset_blocks;
39 : : struct bdevperf_task *task_to_abort;
40 : : enum spdk_bdev_io_type io_type;
41 : : TAILQ_ENTRY(bdevperf_task) link;
42 : : struct spdk_bdev_io_wait_entry bdev_io_wait;
43 : : };
44 : :
45 : : static char *g_workload_type = NULL;
46 : : static int g_io_size = 0;
47 : : /* initialize to invalid value so we can detect if user overrides it. */
48 : : static int g_rw_percentage = -1;
49 : : static bool g_verify = false;
50 : : static bool g_reset = false;
51 : : static bool g_continue_on_failure = false;
52 : : static bool g_abort = false;
53 : : static bool g_error_to_exit = false;
54 : : static int g_queue_depth = 0;
55 : : static uint64_t g_time_in_usec;
56 : : static bool g_summarize_performance = true;
57 : : static uint64_t g_show_performance_period_in_usec = SPDK_SEC_TO_USEC;
58 : : static uint64_t g_show_performance_period_num = 0;
59 : : static uint64_t g_show_performance_ema_period = 0;
60 : : static int g_run_rc = 0;
61 : : static bool g_shutdown = false;
62 : : static uint64_t g_start_tsc;
63 : : static uint64_t g_shutdown_tsc;
64 : : static bool g_zcopy = false;
65 : : static struct spdk_thread *g_main_thread;
66 : : static int g_time_in_sec = 0;
67 : : static bool g_mix_specified = false;
68 : : static const char *g_job_bdev_name;
69 : : static bool g_wait_for_tests = false;
70 : : static struct spdk_jsonrpc_request *g_request = NULL;
71 : : static bool g_multithread_mode = false;
72 : : static int g_timeout_in_sec;
73 : : static struct spdk_conf *g_bdevperf_conf = NULL;
74 : : static const char *g_bdevperf_conf_file = NULL;
75 : : static double g_zipf_theta;
76 : : static bool g_random_map = false;
77 : : static bool g_unique_writes = false;
78 : :
79 : : static struct spdk_cpuset g_all_cpuset;
80 : : static struct spdk_poller *g_perf_timer = NULL;
81 : :
82 : : static void bdevperf_submit_single(struct bdevperf_job *job, struct bdevperf_task *task);
83 : : static void rpc_perform_tests_cb(void);
84 : : static int bdevperf_parse_arg(int ch, char *arg);
85 : : static int verify_test_params(void);
86 : : static void bdevperf_usage(void);
87 : :
88 : : static uint32_t g_bdev_count = 0;
89 : : static uint32_t g_latency_display_level;
90 : :
91 : : static bool g_one_thread_per_lcore = false;
92 : :
93 : : static const double g_latency_cutoffs[] = {
94 : : 0.01,
95 : : 0.10,
96 : : 0.25,
97 : : 0.50,
98 : : 0.75,
99 : : 0.90,
100 : : 0.95,
101 : : 0.98,
102 : : 0.99,
103 : : 0.995,
104 : : 0.999,
105 : : 0.9999,
106 : : 0.99999,
107 : : 0.999999,
108 : : 0.9999999,
109 : : -1,
110 : : };
111 : :
112 : : static const char *g_rpc_log_file_name = NULL;
113 : : static FILE *g_rpc_log_file = NULL;
114 : :
115 : : struct latency_info {
116 : : uint64_t min;
117 : : uint64_t max;
118 : : uint64_t total;
119 : : };
120 : :
121 : :
122 : : enum job_config_rw {
123 : : JOB_CONFIG_RW_READ = 0,
124 : : JOB_CONFIG_RW_WRITE,
125 : : JOB_CONFIG_RW_RANDREAD,
126 : : JOB_CONFIG_RW_RANDWRITE,
127 : : JOB_CONFIG_RW_RW,
128 : : JOB_CONFIG_RW_RANDRW,
129 : : JOB_CONFIG_RW_VERIFY,
130 : : JOB_CONFIG_RW_RESET,
131 : : JOB_CONFIG_RW_UNMAP,
132 : : JOB_CONFIG_RW_FLUSH,
133 : : JOB_CONFIG_RW_WRITE_ZEROES,
134 : : };
135 : :
136 : : struct bdevperf_job {
137 : : char *name;
138 : : struct spdk_bdev *bdev;
139 : : struct spdk_bdev_desc *bdev_desc;
140 : : struct spdk_io_channel *ch;
141 : : TAILQ_ENTRY(bdevperf_job) link;
142 : : struct spdk_thread *thread;
143 : :
144 : : enum job_config_rw workload_type;
145 : : int io_size;
146 : : int rw_percentage;
147 : : bool is_random;
148 : : bool verify;
149 : : bool reset;
150 : : bool continue_on_failure;
151 : : bool unmap;
152 : : bool write_zeroes;
153 : : bool flush;
154 : : bool abort;
155 : : int queue_depth;
156 : : unsigned int seed;
157 : :
158 : : uint64_t io_completed;
159 : : uint64_t io_failed;
160 : : uint64_t io_timeout;
161 : : uint64_t prev_io_completed;
162 : : double ema_io_per_second;
163 : : int current_queue_depth;
164 : : uint64_t size_in_ios;
165 : : uint64_t ios_base;
166 : : uint64_t offset_in_ios;
167 : : uint64_t io_size_blocks;
168 : : uint64_t buf_size;
169 : : uint32_t dif_check_flags;
170 : : bool is_draining;
171 : : bool md_check;
172 : : struct spdk_poller *run_timer;
173 : : struct spdk_poller *reset_timer;
174 : : struct spdk_bit_array *outstanding;
175 : : struct spdk_zipf *zipf;
176 : : TAILQ_HEAD(, bdevperf_task) task_list;
177 : : uint64_t run_time_in_usec;
178 : :
179 : : /* keep channel's histogram data before being destroyed */
180 : : struct spdk_histogram_data *histogram;
181 : : struct spdk_bit_array *random_map;
182 : :
183 : : /* counter used for generating unique write data (-U option) */
184 : : uint32_t write_io_count;
185 : : };
186 : :
187 : : struct spdk_bdevperf {
188 : : TAILQ_HEAD(, bdevperf_job) jobs;
189 : : uint32_t running_jobs;
190 : : };
191 : :
192 : : static struct spdk_bdevperf g_bdevperf = {
193 : : .jobs = TAILQ_HEAD_INITIALIZER(g_bdevperf.jobs),
194 : : .running_jobs = 0,
195 : : };
196 : :
197 : : /* Storing values from a section of job config file */
198 : : struct job_config {
199 : : const char *name;
200 : : const char *filename;
201 : : struct spdk_cpuset cpumask;
202 : : int bs;
203 : : int iodepth;
204 : : int rwmixread;
205 : : uint32_t lcore;
206 : : int64_t offset;
207 : : uint64_t length;
208 : : enum job_config_rw rw;
209 : : TAILQ_ENTRY(job_config) link;
210 : : };
211 : :
212 : : TAILQ_HEAD(, job_config) job_config_list
213 : : = TAILQ_HEAD_INITIALIZER(job_config_list);
214 : :
215 : : static bool g_performance_dump_active = false;
216 : :
217 : : struct bdevperf_stats {
218 : : uint64_t io_time_in_usec;
219 : : double total_io_per_second;
220 : : double total_mb_per_second;
221 : : double total_failed_per_second;
222 : : double total_timeout_per_second;
223 : : double min_latency;
224 : : double max_latency;
225 : : double average_latency;
226 : : uint64_t total_io_completed;
227 : : uint64_t total_tsc;
228 : : };
229 : :
230 : : struct bdevperf_aggregate_stats {
231 : : struct bdevperf_job *current_job;
232 : : struct bdevperf_stats total;
233 : : };
234 : :
235 : : static struct bdevperf_aggregate_stats g_stats = {.total.min_latency = (double)UINT64_MAX};
236 : :
237 : : struct lcore_thread {
238 : : struct spdk_thread *thread;
239 : : uint32_t lcore;
240 : : TAILQ_ENTRY(lcore_thread) link;
241 : : };
242 : :
243 : : TAILQ_HEAD(, lcore_thread) g_lcore_thread_list
244 : : = TAILQ_HEAD_INITIALIZER(g_lcore_thread_list);
245 : :
246 : :
247 : : static char *
248 : 1423 : parse_workload_type(enum job_config_rw ret)
249 : : {
250 [ + + + + : 1423 : switch (ret) {
+ + + + +
+ + - ]
251 : 46 : case JOB_CONFIG_RW_READ:
252 : 54 : return "read";
253 : 112 : case JOB_CONFIG_RW_RANDREAD:
254 : 130 : return "randread";
255 : 27 : case JOB_CONFIG_RW_WRITE:
256 : 30 : return "write";
257 : 180 : case JOB_CONFIG_RW_RANDWRITE:
258 : 180 : return "randwrite";
259 : 657 : case JOB_CONFIG_RW_VERIFY:
260 : 725 : return "verify";
261 : 1 : case JOB_CONFIG_RW_RESET:
262 : 1 : return "reset";
263 : 61 : case JOB_CONFIG_RW_UNMAP:
264 : 61 : return "unmap";
265 : 114 : case JOB_CONFIG_RW_WRITE_ZEROES:
266 : 131 : return "write_zeroes";
267 : 10 : case JOB_CONFIG_RW_FLUSH:
268 : 10 : return "flush";
269 : 32 : case JOB_CONFIG_RW_RW:
270 : 40 : return "rw";
271 : 61 : case JOB_CONFIG_RW_RANDRW:
272 : 61 : return "randrw";
273 : 0 : default:
274 [ # # # # ]: 0 : fprintf(stderr, "wrong workload_type code\n");
275 : 0 : }
276 : :
277 : 0 : return NULL;
278 : 122 : }
279 : :
280 : : /*
281 : : * Cumulative Moving Average (CMA): average of all data up to current
282 : : * Exponential Moving Average (EMA): weighted mean of the previous n data and more weight is given to recent
283 : : * Simple Moving Average (SMA): unweighted mean of the previous n data
284 : : *
285 : : * Bdevperf supports CMA and EMA.
286 : : */
287 : : static double
288 : 4834 : get_cma_io_per_second(struct bdevperf_job *job, uint64_t io_time_in_usec)
289 : : {
290 [ # # # # ]: 4834 : return (double)job->io_completed * SPDK_SEC_TO_USEC / io_time_in_usec;
291 : : }
292 : :
293 : : static double
294 : 0 : get_ema_io_per_second(struct bdevperf_job *job, uint64_t ema_period)
295 : : {
296 : : double io_completed, io_per_second;
297 : :
298 [ # # # # ]: 0 : io_completed = job->io_completed;
299 [ # # # # ]: 0 : io_per_second = (double)(io_completed - job->prev_io_completed) * SPDK_SEC_TO_USEC
300 : 0 : / g_show_performance_period_in_usec;
301 [ # # # # : 0 : job->prev_io_completed = io_completed;
# # ]
302 : :
303 [ # # # # : 0 : job->ema_io_per_second += (io_per_second - job->ema_io_per_second) * 2
# # # # #
# ]
304 : 0 : / (ema_period + 1);
305 [ # # # # ]: 0 : return job->ema_io_per_second;
306 : : }
307 : :
308 : : static void
309 : 35887616 : get_avg_latency(void *ctx, uint64_t start, uint64_t end, uint64_t count,
310 : : uint64_t total, uint64_t so_far)
311 : : {
312 : 35887616 : struct latency_info *latency_info = ctx;
313 : :
314 [ + + ]: 35887616 : if (count == 0) {
315 : 35529233 : return;
316 : : }
317 : :
318 [ # # # # : 358383 : latency_info->total += (start + end) / 2 * count;
# # ]
319 : :
320 [ + + ]: 358383 : if (so_far == count) {
321 [ # # # # ]: 1384 : latency_info->min = start;
322 : 185 : }
323 : :
324 [ + + ]: 358383 : if (so_far == total) {
325 [ # # # # ]: 1384 : latency_info->max = end;
326 : 185 : }
327 : 4298496 : }
328 : :
329 : : static void
330 : 4834 : bdevperf_job_stats_accumulate(struct bdevperf_stats *aggr_stats,
331 : : struct bdevperf_stats *job_stats)
332 : : {
333 [ # # # # : 4834 : aggr_stats->total_io_per_second += job_stats->total_io_per_second;
# # # # ]
334 [ # # # # : 4834 : aggr_stats->total_mb_per_second += job_stats->total_mb_per_second;
# # # # ]
335 [ # # # # : 4834 : aggr_stats->total_failed_per_second += job_stats->total_failed_per_second;
# # # # ]
336 [ # # # # : 4834 : aggr_stats->total_timeout_per_second += job_stats->total_timeout_per_second;
# # # # ]
337 [ # # # # : 4834 : aggr_stats->total_io_completed += job_stats->total_io_completed;
# # # # ]
338 [ # # # # : 4834 : aggr_stats->total_tsc += job_stats->total_tsc;
# # # # ]
339 : :
340 [ + + # # : 4834 : if (job_stats->min_latency < aggr_stats->min_latency) {
# # # # #
# ]
341 [ # # # # : 2195 : aggr_stats->min_latency = job_stats->min_latency;
# # # # ]
342 : 105 : }
343 [ + + # # : 4834 : if (job_stats->max_latency > aggr_stats->max_latency) {
# # # # #
# ]
344 [ # # # # : 567 : aggr_stats->max_latency = job_stats->max_latency;
# # # # ]
345 : 45 : }
346 : 4834 : }
347 : :
348 : : static void
349 : 4834 : bdevperf_job_get_stats(struct bdevperf_job *job,
350 : : struct bdevperf_stats *job_stats,
351 : : uint64_t time_in_usec,
352 : : uint64_t ema_period)
353 : : {
354 : : double io_per_second, mb_per_second, failed_per_second, timeout_per_second;
355 : 4834 : double average_latency = 0.0, min_latency, max_latency;
356 : : uint64_t tsc_rate;
357 : : uint64_t total_io;
358 : 4834 : struct latency_info latency_info = {};
359 : :
360 [ + - ]: 4834 : if (ema_period == 0) {
361 : 4834 : io_per_second = get_cma_io_per_second(job, time_in_usec);
362 : 579 : } else {
363 : 0 : io_per_second = get_ema_io_per_second(job, ema_period);
364 : : }
365 : 4834 : tsc_rate = spdk_get_ticks_hz();
366 [ # # # # ]: 4834 : mb_per_second = io_per_second * job->io_size / (1024 * 1024);
367 : :
368 [ # # # # ]: 4834 : spdk_histogram_data_iterate(job->histogram, get_avg_latency, &latency_info);
369 : :
370 [ # # # # : 4834 : total_io = job->io_completed + job->io_failed;
# # # # ]
371 [ + + ]: 4834 : if (total_io != 0) {
372 [ # # ]: 4818 : average_latency = (double)latency_info.total / total_io * SPDK_SEC_TO_USEC / tsc_rate;
373 : 579 : }
374 : 4834 : min_latency = (double)latency_info.min * SPDK_SEC_TO_USEC / tsc_rate;
375 [ # # ]: 4834 : max_latency = (double)latency_info.max * SPDK_SEC_TO_USEC / tsc_rate;
376 : :
377 [ # # # # ]: 4834 : failed_per_second = (double)job->io_failed * SPDK_SEC_TO_USEC / time_in_usec;
378 [ # # # # ]: 4834 : timeout_per_second = (double)job->io_timeout * SPDK_SEC_TO_USEC / time_in_usec;
379 : :
380 [ # # # # ]: 4834 : job_stats->total_io_per_second = io_per_second;
381 [ # # # # ]: 4834 : job_stats->total_mb_per_second = mb_per_second;
382 [ # # # # ]: 4834 : job_stats->total_failed_per_second = failed_per_second;
383 [ # # # # ]: 4834 : job_stats->total_timeout_per_second = timeout_per_second;
384 [ # # # # ]: 4834 : job_stats->total_io_completed = total_io;
385 [ # # # # : 4834 : job_stats->total_tsc = latency_info.total;
# # ]
386 [ # # # # ]: 4834 : job_stats->average_latency = average_latency;
387 [ # # # # ]: 4834 : job_stats->min_latency = min_latency;
388 [ # # # # ]: 4834 : job_stats->max_latency = max_latency;
389 [ # # # # ]: 4834 : job_stats->io_time_in_usec = time_in_usec;
390 : 4834 : }
391 : :
392 : : static void
393 : 1218 : performance_dump_job_stdout(struct bdevperf_job *job,
394 : : struct bdevperf_stats *job_stats)
395 : : {
396 [ + + + + : 1218 : if (job->workload_type == JOB_CONFIG_RW_RW || job->workload_type == JOB_CONFIG_RW_RANDRW) {
# # # # #
# # # ]
397 [ - + ]: 73 : printf("Job: %s (Core Mask 0x%s, workload: %s, percentage: %d, depth: %d, IO size: %d)\n",
398 [ # # # # : 8 : job->name, spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread)),
# # # # ]
399 [ # # # # : 8 : parse_workload_type(job->workload_type), job->rw_percentage,
# # # # ]
400 [ # # # # : 8 : job->queue_depth, job->io_size);
# # # # ]
401 : 8 : } else {
402 [ - + ]: 1145 : printf("Job: %s (Core Mask 0x%s, workload: %s, depth: %d, IO size: %d)\n",
403 [ # # # # : 106 : job->name, spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread)),
# # # # ]
404 [ # # # # : 106 : parse_workload_type(job->workload_type), job->queue_depth, job->io_size);
# # # # #
# # # ]
405 : : }
406 : :
407 : :
408 [ + + + + : 1218 : if (job->io_failed > 0 && !job->reset && !job->continue_on_failure) {
+ + - + +
+ # # # #
# # # # #
# # # ]
409 [ - + ]: 29 : printf("Job: %s ended in about %.2f seconds with error\n",
410 [ # # # # : 29 : job->name, (double)job->run_time_in_usec / SPDK_SEC_TO_USEC);
# # # # ]
411 : 1 : }
412 [ + + + + : 1218 : if (job->verify) {
# # # # ]
413 [ - + ]: 662 : printf("\t Verification LBA range: start 0x%" PRIx64 " length 0x%" PRIx64 "\n",
414 [ # # # # : 68 : job->ios_base, job->size_in_ios);
# # # # ]
415 : 68 : }
416 : :
417 [ - + ]: 2173 : printf("\t %-20s: %10.2f %10.2f %10.2f",
418 [ # # # # ]: 114 : job->name,
419 [ # # # # ]: 1218 : (float)job_stats->io_time_in_usec / SPDK_SEC_TO_USEC,
420 [ # # # # ]: 114 : job_stats->total_io_per_second,
421 [ # # # # ]: 114 : job_stats->total_mb_per_second);
422 [ - + ]: 1218 : printf(" %10.2f %8.2f",
423 [ # # # # ]: 114 : job_stats->total_failed_per_second,
424 [ # # # # ]: 114 : job_stats->total_timeout_per_second);
425 [ - + ]: 1218 : printf(" %10.2f %10.2f %10.2f\n",
426 [ # # # # ]: 114 : job_stats->average_latency,
427 [ # # # # ]: 114 : job_stats->min_latency,
428 [ # # # # ]: 114 : job_stats->max_latency);
429 : 1218 : }
430 : :
431 : : static void
432 : 205 : performance_dump_job_json(struct bdevperf_job *job,
433 : : struct spdk_json_write_ctx *w,
434 : : struct bdevperf_stats *job_stats)
435 : : {
436 : 205 : char core_mask_string[BDEVPERF_MAX_COREMASK_STRING] = {0};
437 : :
438 [ # # # # ]: 205 : spdk_json_write_named_string(w, "job", job->name);
439 [ - + ]: 213 : snprintf(core_mask_string, BDEVPERF_MAX_COREMASK_STRING,
440 [ # # # # ]: 8 : "0x%s", spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread)));
441 : 205 : spdk_json_write_named_string(w, "core_mask", core_mask_string);
442 [ # # # # ]: 205 : spdk_json_write_named_string(w, "workload", parse_workload_type(job->workload_type));
443 : :
444 [ + - + + : 205 : if (job->workload_type == JOB_CONFIG_RW_RW || job->workload_type == JOB_CONFIG_RW_RANDRW) {
# # # # #
# # # ]
445 [ # # # # ]: 28 : spdk_json_write_named_uint32(w, "percentage", job->rw_percentage);
446 : 0 : }
447 : :
448 [ - + + + ]: 205 : if (g_shutdown) {
449 : 8 : spdk_json_write_named_string(w, "status", "terminated");
450 [ + + + + : 197 : } else if (job->io_failed > 0 && !job->reset && !job->continue_on_failure) {
+ + - + -
+ # # # #
# # # # #
# # # ]
451 : 0 : spdk_json_write_named_string(w, "status", "failed");
452 : 0 : } else {
453 : 197 : spdk_json_write_named_string(w, "status", "finished");
454 : : }
455 : :
456 [ + + + + : 205 : if (job->verify) {
# # # # ]
457 : 64 : spdk_json_write_named_object_begin(w, "verify_range");
458 [ # # # # ]: 64 : spdk_json_write_named_uint64(w, "start", job->ios_base);
459 [ # # # # ]: 64 : spdk_json_write_named_uint64(w, "length", job->size_in_ios);
460 : 64 : spdk_json_write_object_end(w);
461 : 0 : }
462 : :
463 [ # # # # ]: 205 : spdk_json_write_named_uint32(w, "queue_depth", job->queue_depth);
464 [ # # # # ]: 205 : spdk_json_write_named_uint32(w, "io_size", job->io_size);
465 [ # # # # ]: 205 : spdk_json_write_named_double(w, "runtime", (double)job_stats->io_time_in_usec / SPDK_SEC_TO_USEC);
466 [ # # # # ]: 205 : spdk_json_write_named_double(w, "iops", job_stats->total_io_per_second);
467 [ # # # # ]: 205 : spdk_json_write_named_double(w, "mibps", job_stats->total_mb_per_second);
468 [ # # # # ]: 205 : spdk_json_write_named_uint64(w, "io_failed", job->io_failed);
469 [ # # # # ]: 205 : spdk_json_write_named_uint64(w, "io_timeout", job->io_timeout);
470 [ # # # # ]: 205 : spdk_json_write_named_double(w, "avg_latency_us", job_stats->average_latency);
471 [ # # # # ]: 205 : spdk_json_write_named_double(w, "min_latency_us", job_stats->min_latency);
472 [ # # # # ]: 205 : spdk_json_write_named_double(w, "max_latency_us", job_stats->max_latency);
473 : 205 : }
474 : :
475 : : static void
476 : 9786144 : generate_data(struct bdevperf_job *job, void *buf, void *md_buf, bool unique)
477 : : {
478 : 9786144 : int offset_blocks = 0, md_offset, data_block_size, inner_offset;
479 [ # # # # ]: 9786144 : int buf_len = job->buf_size;
480 [ # # # # ]: 9786144 : int block_size = spdk_bdev_get_block_size(job->bdev);
481 [ # # # # ]: 9786144 : int md_size = spdk_bdev_get_md_size(job->bdev);
482 [ # # # # ]: 9786144 : int num_blocks = job->io_size_blocks;
483 : :
484 [ - + # # ]: 9786144 : if (buf_len < num_blocks * block_size) {
485 : 0 : return;
486 : : }
487 : :
488 [ + + ]: 9786144 : if (md_buf == NULL) {
489 [ # # ]: 9755563 : data_block_size = block_size - md_size;
490 [ # # ]: 9755563 : md_buf = (char *)buf + data_block_size;
491 : 9755563 : md_offset = block_size;
492 : 899849 : } else {
493 : 30581 : data_block_size = block_size;
494 : 30581 : md_offset = md_size;
495 : : }
496 : :
497 [ + + # # ]: 9786144 : if (unique) {
498 [ # # ]: 1418 : uint64_t io_count = job->write_io_count++;
499 : : unsigned int i;
500 : :
501 [ - + - - : 1418 : assert(md_size == 0 || md_size >= (int)sizeof(uint64_t));
# # ]
502 : :
503 [ + + ]: 8713610 : while (offset_blocks < num_blocks) {
504 : 8712192 : inner_offset = 0;
505 [ + + ]: 566292480 : while (inner_offset < data_block_size) {
506 [ # # # # : 557580288 : *(uint64_t *)buf = (io_count << 32) | (offset_blocks + inner_offset);
# # ]
507 : 557580288 : inner_offset += sizeof(uint64_t);
508 : 557580288 : buf += sizeof(uint64_t);
509 : : }
510 [ - + # # ]: 8712192 : for (i = 0; i < md_size / sizeof(uint64_t); i++) {
511 [ # # # # : 0 : ((uint64_t *)md_buf)[i] = (io_count << 32) | offset_blocks;
# # ]
512 : 0 : }
513 : 8712192 : md_buf += md_offset;
514 [ # # ]: 8712192 : offset_blocks++;
515 : : }
516 : 1418 : return;
517 : : }
518 : :
519 [ + + ]: 91175547 : while (offset_blocks < num_blocks) {
520 : 81391179 : inner_offset = 0;
521 [ + + ]:16324228066 : while (inner_offset < data_block_size) {
522 [ # # # # ]:16242836887 : *(uint32_t *)buf = offset_blocks + inner_offset;
523 :16242836887 : inner_offset += sizeof(uint32_t);
524 :16242836887 : buf += sizeof(uint32_t);
525 : : }
526 [ - + ]: 81391179 : memset(md_buf, offset_blocks, md_size);
527 : 81391179 : md_buf += md_offset;
528 [ # # ]: 81391179 : offset_blocks++;
529 : : }
530 : 899670 : }
531 : :
532 : : static bool
533 : 0 : copy_data(void *wr_buf, int wr_buf_len, void *rd_buf, int rd_buf_len, int block_size,
534 : : void *wr_md_buf, void *rd_md_buf, int md_size, int num_blocks)
535 : : {
536 [ # # # # : 0 : if (wr_buf_len < num_blocks * block_size || rd_buf_len < num_blocks * block_size) {
# # # # ]
537 : 0 : return false;
538 : : }
539 : :
540 [ # # # # ]: 0 : assert((wr_md_buf != NULL) == (rd_md_buf != NULL));
541 : :
542 [ # # # # : 0 : memcpy(wr_buf, rd_buf, block_size * num_blocks);
# # ]
543 : :
544 [ # # ]: 0 : if (wr_md_buf != NULL) {
545 [ # # # # : 0 : memcpy(wr_md_buf, rd_md_buf, md_size * num_blocks);
# # ]
546 : 0 : }
547 : :
548 : 0 : return true;
549 : 0 : }
550 : :
551 : : static bool
552 : 9351253 : verify_data(void *wr_buf, int wr_buf_len, void *rd_buf, int rd_buf_len, int block_size,
553 : : void *wr_md_buf, void *rd_md_buf, int md_size, int num_blocks, bool md_check)
554 : : {
555 : 9351253 : int offset_blocks = 0, md_offset, data_block_size;
556 : :
557 [ + + + + : 9351253 : if (wr_buf_len < num_blocks * block_size || rd_buf_len < num_blocks * block_size) {
# # # # ]
558 : 9230 : return false;
559 : : }
560 : :
561 [ + + # # ]: 9351253 : assert((wr_md_buf != NULL) == (rd_md_buf != NULL));
562 : :
563 [ + + ]: 9351253 : if (wr_md_buf == NULL) {
564 [ # # ]: 9316264 : data_block_size = block_size - md_size;
565 [ # # ]: 9316264 : wr_md_buf = (char *)wr_buf + data_block_size;
566 [ # # ]: 9316264 : rd_md_buf = (char *)rd_buf + data_block_size;
567 : 9316264 : md_offset = block_size;
568 : 900199 : } else {
569 : 34989 : data_block_size = block_size;
570 : 34989 : md_offset = md_size;
571 : : }
572 : :
573 [ + + ]: 86794030 : while (offset_blocks < num_blocks) {
574 [ + + - + : 77442777 : if (memcmp(wr_buf, rd_buf, data_block_size) != 0) {
- + ]
575 : 0 : printf("data_block_size %d, num_blocks %d, offset %d\n", data_block_size, num_blocks,
576 : 0 : offset_blocks);
577 : 0 : spdk_log_dump(stdout, "rd_buf", rd_buf, data_block_size);
578 : 0 : spdk_log_dump(stdout, "wr_buf", wr_buf, data_block_size);
579 : 0 : return false;
580 : : }
581 : :
582 : 77442777 : wr_buf += block_size;
583 : 77442777 : rd_buf += block_size;
584 : :
585 [ + + # # ]: 77442777 : if (md_check) {
586 [ + + - + : 77440805 : if (memcmp(wr_md_buf, rd_md_buf, md_size) != 0) {
- + ]
587 : 0 : printf("md_size %d, num_blocks %d, offset %d\n", md_size, num_blocks, offset_blocks);
588 : 0 : spdk_log_dump(stdout, "rd_md_buf", rd_md_buf, md_size);
589 : 0 : spdk_log_dump(stdout, "wr_md_buf", wr_md_buf, md_size);
590 : 0 : return false;
591 : : }
592 : :
593 : 77440805 : wr_md_buf += md_offset;
594 : 77440805 : rd_md_buf += md_offset;
595 : 13887895 : }
596 : :
597 [ # # ]: 77442777 : offset_blocks++;
598 : : }
599 : :
600 : 9351253 : return true;
601 : 904786 : }
602 : :
603 : : static void
604 : 937 : free_job_config(void)
605 : : {
606 : : struct job_config *config, *tmp;
607 : :
608 : 937 : spdk_conf_free(g_bdevperf_conf);
609 : 937 : g_bdevperf_conf = NULL;
610 : :
611 [ + + + + : 2015 : TAILQ_FOREACH_SAFE(config, &job_config_list, link, tmp) {
# # # # #
# ]
612 [ + + # # : 1078 : TAILQ_REMOVE(&job_config_list, config, link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
613 : 1078 : free(config);
614 : 110 : }
615 : 937 : }
616 : :
617 : : static void
618 : 1098 : bdevperf_job_free(struct bdevperf_job *job)
619 : : {
620 [ - + # # : 1098 : if (job->bdev_desc != NULL) {
# # ]
621 [ # # # # ]: 0 : spdk_bdev_close(job->bdev_desc);
622 : 0 : }
623 : :
624 [ # # # # ]: 1098 : spdk_histogram_data_free(job->histogram);
625 [ # # ]: 1098 : spdk_bit_array_free(&job->outstanding);
626 [ # # ]: 1098 : spdk_bit_array_free(&job->random_map);
627 [ # # ]: 1098 : spdk_zipf_free(&job->zipf);
628 [ # # # # ]: 1098 : free(job->name);
629 : 1098 : free(job);
630 : 1098 : }
631 : :
632 : : static void
633 : 1098 : job_thread_exit(void *ctx)
634 : : {
635 : 1098 : spdk_thread_exit(spdk_get_thread());
636 : 1098 : }
637 : :
638 : : static void
639 : 0 : check_cutoff(void *ctx, uint64_t start, uint64_t end, uint64_t count,
640 : : uint64_t total, uint64_t so_far)
641 : : {
642 : : double so_far_pct;
643 : 0 : double **cutoff = ctx;
644 : : uint64_t tsc_rate;
645 : :
646 [ # # ]: 0 : if (count == 0) {
647 : 0 : return;
648 : : }
649 : :
650 : 0 : tsc_rate = spdk_get_ticks_hz();
651 : 0 : so_far_pct = (double)so_far / total;
652 [ # # # # : 0 : while (so_far_pct >= **cutoff && **cutoff > 0) {
# # # # #
# # # ]
653 [ # # # # ]: 0 : printf("%9.5f%% : %9.3fus\n", **cutoff * 100, (double)end * SPDK_SEC_TO_USEC / tsc_rate);
654 [ # # ]: 0 : (*cutoff)++;
655 : : }
656 : 0 : }
657 : :
658 : : static void
659 : 0 : print_bucket(void *ctx, uint64_t start, uint64_t end, uint64_t count,
660 : : uint64_t total, uint64_t so_far)
661 : : {
662 : : double so_far_pct;
663 : : uint64_t tsc_rate;
664 : :
665 [ # # ]: 0 : if (count == 0) {
666 : 0 : return;
667 : : }
668 : :
669 : 0 : tsc_rate = spdk_get_ticks_hz();
670 : 0 : so_far_pct = (double)so_far * 100 / total;
671 : 0 : printf("%9.3f - %9.3f: %9.4f%% (%9ju)\n",
672 : 0 : (double)start * SPDK_SEC_TO_USEC / tsc_rate,
673 [ # # ]: 0 : (double)end * SPDK_SEC_TO_USEC / tsc_rate,
674 : 0 : so_far_pct, count);
675 : 0 : }
676 : :
677 : : static void
678 : 6583 : bdevperf_test_done(void *ctx)
679 : : {
680 : : struct bdevperf_job *job, *jtmp;
681 : : struct bdevperf_task *task, *ttmp;
682 : : struct lcore_thread *lthread, *lttmp;
683 : 6583 : double average_latency = 0.0;
684 : : uint64_t time_in_usec;
685 : : int rc;
686 : 6583 : struct spdk_json_write_ctx *w = NULL;
687 : 6583 : struct bdevperf_stats job_stats = {0};
688 : 4960 : struct spdk_cpuset cpu_mask;
689 : :
690 [ + + ]: 6583 : if (g_time_in_usec) {
691 [ # # # # ]: 6575 : g_stats.total.io_time_in_usec = g_time_in_usec;
692 : :
693 [ + + + + : 6575 : if (!g_run_rc && g_performance_dump_active) {
+ + ]
694 : 6042 : spdk_thread_send_msg(spdk_get_thread(), bdevperf_test_done, NULL);
695 : 6042 : return;
696 : : }
697 : 19 : }
698 : :
699 : 541 : spdk_poller_unregister(&g_perf_timer);
700 : :
701 [ + + + + ]: 541 : if (g_shutdown) {
702 : 196 : g_shutdown_tsc = spdk_get_ticks() - g_start_tsc;
703 [ - + ]: 196 : time_in_usec = g_shutdown_tsc * SPDK_SEC_TO_USEC / spdk_get_ticks_hz();
704 [ + + ]: 196 : g_time_in_usec = (g_time_in_usec > time_in_usec) ? time_in_usec : g_time_in_usec;
705 : 196 : printf("Received shutdown signal, test time was about %.6f seconds\n",
706 [ - + ]: 196 : (double)g_time_in_usec / SPDK_SEC_TO_USEC);
707 : 4 : }
708 : : /* Send RPC response if g_run_rc indicate success, or shutdown request was sent to bdevperf.
709 : : * rpc_perform_tests_cb will send error response in case of error.
710 : : */
711 [ + + + + : 525 : if ((g_run_rc == 0 || g_shutdown) && g_request) {
+ + + + ]
712 : 165 : w = spdk_jsonrpc_begin_result(g_request);
713 : 165 : spdk_json_write_object_begin(w);
714 : 165 : spdk_json_write_named_array_begin(w, "results");
715 : 4 : }
716 : :
717 [ - + ]: 533 : printf("\n%*s\n", 107, "Latency(us)");
718 [ - + ]: 533 : printf("\r %-*s: %10s %10s %10s %10s %8s %10s %10s %10s\n",
719 : : 28, "Device Information", "runtime(s)", "IOPS", "MiB/s", "Fail/s", "TO/s", "Average", "min", "max");
720 : :
721 : :
722 : 533 : spdk_cpuset_zero(&cpu_mask);
723 [ + + + + : 1631 : TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) {
# # # # #
# ]
724 [ # # # # ]: 1098 : spdk_cpuset_or(&cpu_mask, spdk_thread_get_cpumask(job->thread));
725 [ - + ]: 1098 : memset(&job_stats, 0, sizeof(job_stats));
726 [ # # # # ]: 1098 : bdevperf_job_get_stats(job, &job_stats, job->run_time_in_usec, 0);
727 : 1098 : bdevperf_job_stats_accumulate(&g_stats.total, &job_stats);
728 : 1098 : performance_dump_job_stdout(job, &job_stats);
729 [ + + ]: 1098 : if (w) {
730 : 205 : spdk_json_write_object_begin(w);
731 : 205 : performance_dump_job_json(job, w, &job_stats);
732 : 205 : spdk_json_write_object_end(w);
733 : 8 : }
734 : 114 : }
735 : :
736 [ + + ]: 533 : if (w) {
737 : 165 : spdk_json_write_array_end(w);
738 : 165 : spdk_json_write_named_uint32(w, "core_count", spdk_cpuset_count(&cpu_mask));
739 : 165 : spdk_json_write_object_end(w);
740 : 165 : spdk_jsonrpc_end_result(g_request, w);
741 : 4 : }
742 [ - + ]: 533 : printf("\r =================================================================================="
743 : : "=================================\n");
744 [ - + ]: 533 : printf("\r %-28s: %10s %10.2f %10.2f",
745 [ # # # # : 19 : "Total", "", g_stats.total.total_io_per_second, g_stats.total.total_mb_per_second);
# # # # ]
746 [ - + ]: 533 : printf(" %10.2f %8.2f",
747 [ # # # # : 19 : g_stats.total.total_failed_per_second, g_stats.total.total_timeout_per_second);
# # # # ]
748 : :
749 [ + + # # : 533 : if (g_stats.total.total_io_completed != 0) {
# # ]
750 [ # # # # : 696 : average_latency = ((double)g_stats.total.total_tsc / g_stats.total.total_io_completed) *
# # # # ]
751 : 348 : SPDK_SEC_TO_USEC /
752 : 348 : spdk_get_ticks_hz();
753 : 15 : }
754 [ - + # # ]: 552 : printf(" %10.2f %10.2f %10.2f\n", average_latency, g_stats.total.min_latency,
755 [ # # # # ]: 19 : g_stats.total.max_latency);
756 : :
757 [ - + - - : 533 : if (g_latency_display_level == 0 || g_stats.total.total_io_completed == 0) {
# # # # ]
758 : 533 : goto clean;
759 : : }
760 : :
761 [ # # ]: 0 : printf("\n Latency summary\n");
762 [ # # # # : 0 : TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) {
# # # # #
# ]
763 [ # # ]: 0 : printf("\r =============================================\n");
764 [ # # # # ]: 0 : printf("\r Job: %s (Core Mask 0x%s)\n", job->name,
765 [ # # # # ]: 0 : spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread)));
766 : :
767 : 0 : const double *cutoff = g_latency_cutoffs;
768 : :
769 [ # # # # ]: 0 : spdk_histogram_data_iterate(job->histogram, check_cutoff, &cutoff);
770 : :
771 : 0 : printf("\n");
772 : 0 : }
773 : :
774 [ # # ]: 0 : if (g_latency_display_level == 1) {
775 : 0 : goto clean;
776 : : }
777 : :
778 [ # # ]: 0 : printf("\r Latency histogram\n");
779 [ # # # # : 0 : TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) {
# # # # #
# ]
780 [ # # ]: 0 : printf("\r =============================================\n");
781 [ # # # # ]: 0 : printf("\r Job: %s (Core Mask 0x%s)\n", job->name,
782 [ # # # # ]: 0 : spdk_cpuset_fmt(spdk_thread_get_cpumask(job->thread)));
783 : :
784 [ # # # # ]: 0 : spdk_histogram_data_iterate(job->histogram, print_bucket, NULL);
785 : 0 : printf("\n");
786 : 0 : }
787 : :
788 : 152 : clean:
789 : 533 : fflush(stdout);
790 : :
791 [ + + + + : 1631 : TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, jtmp) {
# # # # #
# ]
792 [ + + # # : 1098 : TAILQ_REMOVE(&g_bdevperf.jobs, job, link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
793 : :
794 [ + + + - ]: 1098 : if (!g_one_thread_per_lcore) {
795 [ # # # # ]: 1098 : spdk_thread_send_msg(job->thread, job_thread_exit, NULL);
796 : 114 : }
797 : :
798 [ + + + + : 139524 : TAILQ_FOREACH_SAFE(task, &job->task_list, link, ttmp) {
# # # # #
# # # # #
# # ]
799 [ + + # # : 138426 : TAILQ_REMOVE(&job->task_list, task, link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
800 [ # # # # ]: 138426 : spdk_free(task->buf);
801 [ # # # # ]: 138426 : spdk_free(task->verify_buf);
802 [ # # # # ]: 138426 : spdk_free(task->md_buf);
803 [ # # # # ]: 138426 : spdk_free(task->verify_md_buf);
804 : 138426 : free(task);
805 : 15708 : }
806 : :
807 : 1098 : bdevperf_job_free(job);
808 : 114 : }
809 : :
810 [ + + - + ]: 533 : if (g_one_thread_per_lcore) {
811 [ # # # # : 0 : TAILQ_FOREACH_SAFE(lthread, &g_lcore_thread_list, link, lttmp) {
# # # # #
# ]
812 [ # # # # : 0 : TAILQ_REMOVE(&g_lcore_thread_list, lthread, link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
813 [ # # # # ]: 0 : spdk_thread_send_msg(lthread->thread, job_thread_exit, NULL);
814 : 0 : free(lthread);
815 : 0 : }
816 : 0 : }
817 : :
818 [ + + ]: 533 : if (g_bdevperf_conf == NULL) {
819 : 513 : free_job_config();
820 : 15 : }
821 : :
822 : 533 : rc = g_run_rc;
823 [ + + + + : 533 : if (g_request && !g_shutdown) {
+ + ]
824 : 162 : rpc_perform_tests_cb();
825 [ + + ]: 162 : if (rc != 0) {
826 : 5 : spdk_app_stop(rc);
827 : 1 : }
828 : 5 : } else {
829 : 371 : spdk_app_stop(rc);
830 : : }
831 : 25 : }
832 : :
833 : : static void
834 : 1098 : bdevperf_job_end(void *ctx)
835 : : {
836 : 1098 : struct bdevperf_job *job = ctx;
837 : :
838 [ + + # # ]: 1098 : assert(g_main_thread == spdk_get_thread());
839 : :
840 [ + + # # : 1098 : if (job->bdev_desc != NULL) {
# # ]
841 [ # # # # ]: 1098 : spdk_bdev_close(job->bdev_desc);
842 [ # # # # ]: 1098 : job->bdev_desc = NULL;
843 : 114 : }
844 : :
845 [ + + ]: 1098 : if (--g_bdevperf.running_jobs == 0) {
846 : 348 : bdevperf_test_done(NULL);
847 : 15 : }
848 : 1098 : }
849 : :
850 : : static void
851 : 1098 : bdevperf_channel_get_histogram_cb(void *cb_arg, int status, struct spdk_histogram_data *histogram)
852 : : {
853 : 1098 : struct spdk_histogram_data *job_hist = cb_arg;
854 : :
855 [ + - ]: 1098 : if (status == 0) {
856 : 1098 : spdk_histogram_data_merge(job_hist, histogram);
857 : 114 : }
858 : 1098 : }
859 : :
860 : : static void
861 : 1098 : bdevperf_job_empty(struct bdevperf_job *job)
862 : : {
863 : 1098 : uint64_t end_tsc = 0;
864 : :
865 : 1098 : end_tsc = spdk_get_ticks() - g_start_tsc;
866 [ - + # # : 1098 : job->run_time_in_usec = end_tsc * SPDK_SEC_TO_USEC / spdk_get_ticks_hz();
# # ]
867 : : /* keep histogram info before channel is destroyed */
868 [ # # # # ]: 1212 : spdk_bdev_channel_get_histogram(job->ch, bdevperf_channel_get_histogram_cb,
869 [ # # # # ]: 1098 : job->histogram);
870 [ # # # # ]: 1098 : spdk_put_io_channel(job->ch);
871 : 1098 : spdk_thread_send_msg(g_main_thread, bdevperf_job_end, job);
872 : 1098 : }
873 : :
874 : : static void
875 : 138415 : bdevperf_end_task(struct bdevperf_task *task)
876 : : {
877 [ # # # # ]: 138415 : struct bdevperf_job *job = task->job;
878 : :
879 [ # # # # : 138415 : TAILQ_INSERT_TAIL(&job->task_list, task, link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
880 [ + + + - : 138415 : if (job->is_draining) {
# # # # ]
881 [ + + # # : 138416 : if (job->current_queue_depth == 0) {
# # ]
882 : 1098 : bdevperf_job_empty(job);
883 : 114 : }
884 : 15699 : }
885 : 138417 : }
886 : :
887 : : static void
888 : 374861 : bdevperf_queue_io_wait_with_cb(struct bdevperf_task *task, spdk_bdev_io_wait_cb cb_fn)
889 : : {
890 [ # # # # ]: 374861 : struct bdevperf_job *job = task->job;
891 : :
892 [ # # # # : 374861 : task->bdev_io_wait.bdev = job->bdev;
# # # # #
# ]
893 [ # # # # : 374861 : task->bdev_io_wait.cb_fn = cb_fn;
# # ]
894 [ # # # # : 374861 : task->bdev_io_wait.cb_arg = task;
# # ]
895 [ # # # # : 374861 : spdk_bdev_queue_io_wait(job->bdev, job->ch, &task->bdev_io_wait);
# # # # #
# ]
896 : 374861 : }
897 : :
898 : : static int
899 : 2823 : bdevperf_job_drain(void *ctx)
900 : : {
901 : 2823 : struct bdevperf_job *job = ctx;
902 : :
903 [ # # ]: 2823 : spdk_poller_unregister(&job->run_timer);
904 [ + + + + : 2823 : if (job->reset) {
# # # # ]
905 [ # # ]: 1 : spdk_poller_unregister(&job->reset_timer);
906 : 0 : }
907 : :
908 [ # # # # ]: 2823 : job->is_draining = true;
909 : :
910 : 2823 : return -1;
911 : : }
912 : :
913 : : static int
914 : 973 : bdevperf_job_drain_timer(void *ctx)
915 : : {
916 : 973 : struct bdevperf_job *job = ctx;
917 : :
918 : 973 : bdevperf_job_drain(ctx);
919 [ - + # # : 973 : if (job->current_queue_depth == 0) {
# # ]
920 : 0 : bdevperf_job_empty(job);
921 : 0 : }
922 : :
923 : 973 : return SPDK_POLLER_BUSY;
924 : : }
925 : :
926 : : static void
927 : 0 : bdevperf_abort_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
928 : : {
929 : 0 : struct bdevperf_task *task = cb_arg;
930 [ # # # # ]: 0 : struct bdevperf_job *job = task->job;
931 : :
932 [ # # # # ]: 0 : job->current_queue_depth--;
933 : :
934 [ # # # # ]: 0 : if (success) {
935 [ # # ]: 0 : job->io_completed++;
936 : 0 : } else {
937 [ # # ]: 0 : job->io_failed++;
938 [ # # # # : 0 : if (!job->continue_on_failure) {
# # # # ]
939 : 0 : bdevperf_job_drain(job);
940 : 0 : g_run_rc = -1;
941 : 0 : }
942 : : }
943 : :
944 : 0 : spdk_bdev_free_io(bdev_io);
945 : 0 : bdevperf_end_task(task);
946 : 0 : }
947 : :
948 : : static int
949 : 0 : bdevperf_verify_dif(struct bdevperf_task *task)
950 : : {
951 [ # # # # ]: 0 : struct bdevperf_job *job = task->job;
952 [ # # # # ]: 0 : struct spdk_bdev *bdev = job->bdev;
953 : 0 : struct spdk_dif_ctx dif_ctx;
954 : 0 : struct spdk_dif_error err_blk = {};
955 : : int rc;
956 : 0 : struct spdk_dif_ctx_init_ext_opts dif_opts;
957 : :
958 : 0 : dif_opts.size = SPDK_SIZEOF(&dif_opts, dif_pi_format);
959 [ # # ]: 0 : dif_opts.dif_pi_format = spdk_bdev_get_dif_pi_format(bdev);
960 : 0 : rc = spdk_dif_ctx_init(&dif_ctx,
961 : 0 : spdk_bdev_get_block_size(bdev),
962 : 0 : spdk_bdev_get_md_size(bdev),
963 : 0 : spdk_bdev_is_md_interleaved(bdev),
964 : 0 : spdk_bdev_is_dif_head_of_md(bdev),
965 : 0 : spdk_bdev_get_dif_type(bdev),
966 [ # # # # ]: 0 : job->dif_check_flags,
967 [ # # # # ]: 0 : task->offset_blocks, 0, 0, 0, 0, &dif_opts);
968 [ # # ]: 0 : if (rc != 0) {
969 [ # # # # ]: 0 : fprintf(stderr, "Initialization of DIF context failed\n");
970 : 0 : return rc;
971 : : }
972 : :
973 [ # # ]: 0 : if (spdk_bdev_is_md_interleaved(bdev)) {
974 [ # # # # : 0 : rc = spdk_dif_verify(&task->iov, 1, job->io_size_blocks, &dif_ctx, &err_blk);
# # ]
975 : 0 : } else {
976 : 0 : struct iovec md_iov = {
977 [ # # # # ]: 0 : .iov_base = task->md_buf,
978 [ # # # # ]: 0 : .iov_len = spdk_bdev_get_md_size(bdev) * job->io_size_blocks,
979 : : };
980 : :
981 [ # # # # : 0 : rc = spdk_dix_verify(&task->iov, 1, &md_iov, job->io_size_blocks, &dif_ctx, &err_blk);
# # ]
982 : : }
983 : :
984 [ # # ]: 0 : if (rc != 0) {
985 [ # # ]: 0 : fprintf(stderr, "DIF/DIX error detected. type=%d, offset=%" PRIu32 "\n",
986 [ # # ]: 0 : err_blk.err_type, err_blk.err_offset);
987 : 0 : }
988 : :
989 : 0 : return rc;
990 : 0 : }
991 : :
992 : : static void
993 : 77830662 : bdevperf_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
994 : : {
995 : : struct bdevperf_job *job;
996 : 77830662 : struct bdevperf_task *task = cb_arg;
997 : : uint64_t offset_in_ios;
998 : : int rc;
999 : :
1000 [ # # # # ]: 77830662 : job = task->job;
1001 : :
1002 [ + + + + ]: 77830662 : if (g_error_to_exit == true) {
1003 : 5792 : bdevperf_job_drain(job);
1004 [ + + # # ]: 77828945 : } else if (!success) {
1005 [ + + + + : 436936 : if (!job->reset && !job->continue_on_failure) {
- + + + #
# # # # #
# # ]
1006 : 12 : bdevperf_job_drain(job);
1007 : 12 : g_run_rc = -1;
1008 : 12 : g_error_to_exit = true;
1009 [ - + ]: 12 : printf("task offset: %" PRIu64 " on job bdev=%s fails\n",
1010 [ # # # # : 1 : task->offset_blocks, job->name);
# # # # ]
1011 : 1 : }
1012 [ + + + + : 77387940 : } else if (job->verify || job->reset) {
- + - + #
# # # # #
# # ]
1013 [ + + + - : 18701260 : if (!verify_data(task->buf, job->buf_size,
# # # # #
# ]
1014 [ # # # # : 9350630 : task->iov.iov_base, job->buf_size,
# # # # #
# ]
1015 [ # # # # ]: 9350630 : spdk_bdev_get_block_size(job->bdev),
1016 [ # # # # ]: 904163 : task->md_buf, spdk_bdev_io_get_md_buf(bdev_io),
1017 [ # # # # ]: 9350630 : spdk_bdev_get_md_size(job->bdev),
1018 [ - + # # : 9350630 : job->io_size_blocks, job->md_check)) {
# # # # #
# ]
1019 [ # # # # : 0 : printf("Buffer mismatch! Target: %s Disk Offset: %" PRIu64 "\n", job->name, task->offset_blocks);
# # # # ]
1020 : 0 : bdevperf_job_drain(job);
1021 : 0 : g_run_rc = -1;
1022 : 0 : }
1023 [ + + # # : 68941467 : } else if (job->dif_check_flags != 0) {
# # ]
1024 [ # # # # : 0 : if (task->io_type == SPDK_BDEV_IO_TYPE_READ && spdk_bdev_get_md_size(job->bdev) != 0) {
# # # # #
# # # ]
1025 : 0 : rc = bdevperf_verify_dif(task);
1026 [ # # ]: 0 : if (rc != 0) {
1027 [ # # ]: 0 : printf("DIF error detected. task offset: %" PRIu64 " on job bdev=%s\n",
1028 [ # # # # : 0 : task->offset_blocks, job->name);
# # # # ]
1029 : :
1030 : 0 : success = false;
1031 [ # # # # : 0 : if (!job->reset && !job->continue_on_failure) {
# # # # #
# # # # #
# # ]
1032 : 0 : bdevperf_job_drain(job);
1033 : 0 : g_run_rc = -1;
1034 : 0 : g_error_to_exit = true;
1035 : 0 : }
1036 : 0 : }
1037 : 0 : }
1038 : 0 : }
1039 : :
1040 [ # # # # ]: 77830662 : job->current_queue_depth--;
1041 : :
1042 [ + + # # ]: 77830662 : if (success) {
1043 [ # # ]: 77384027 : job->io_completed++;
1044 : 34924225 : } else {
1045 [ # # ]: 438485 : job->io_failed++;
1046 : : }
1047 : :
1048 [ + + + + : 77822512 : if (job->verify) {
# # # # ]
1049 [ + + - + : 9787950 : assert(task->offset_blocks / job->io_size_blocks >= job->ios_base);
# # # # #
# # # # #
# # # # ]
1050 [ - + # # : 9787950 : offset_in_ios = task->offset_blocks / job->io_size_blocks - job->ios_base;
# # # # #
# # # #
# ]
1051 : :
1052 [ - + # # : 9787950 : assert(spdk_bit_array_get(job->outstanding, offset_in_ios) == true);
# # # # ]
1053 [ # # # # ]: 9787950 : spdk_bit_array_clear(job->outstanding, offset_in_ios);
1054 : 903252 : }
1055 : :
1056 : 77822512 : spdk_bdev_free_io(bdev_io);
1057 : :
1058 : : /*
1059 : : * is_draining indicates when time has expired for the test run
1060 : : * and we are just waiting for the previously submitted I/O
1061 : : * to complete. In this case, do not submit a new I/O to replace
1062 : : * the one just completed.
1063 : : */
1064 [ + + + + : 77822512 : if (!job->is_draining) {
# # # # ]
1065 : 77684097 : bdevperf_submit_single(job, task);
1066 : 34908537 : } else {
1067 : 138415 : bdevperf_end_task(task);
1068 : : }
1069 : 77822512 : }
1070 : :
1071 : : static void
1072 : 9347687 : bdevperf_verify_submit_read(void *cb_arg)
1073 : : {
1074 : : struct bdevperf_job *job;
1075 : 9347687 : struct bdevperf_task *task = cb_arg;
1076 : : int rc;
1077 : :
1078 [ # # # # ]: 9347687 : job = task->job;
1079 : :
1080 [ # # # # : 9347687 : task->iov.iov_base = task->verify_buf;
# # # # #
# ]
1081 [ # # # # : 9347687 : task->iov.iov_len = job->buf_size;
# # # # #
# ]
1082 : :
1083 : : /* Read the data back in */
1084 [ # # # # : 10246934 : rc = spdk_bdev_readv_blocks_with_md(job->bdev_desc, job->ch, &task->iov, 1, task->verify_md_buf,
# # # # #
# # # #
# ]
1085 [ # # # # : 899247 : task->offset_blocks, job->io_size_blocks,
# # # # ]
1086 : 899247 : bdevperf_complete, task);
1087 : :
1088 [ - + ]: 9347687 : if (rc == -ENOMEM) {
1089 : 0 : bdevperf_queue_io_wait_with_cb(task, bdevperf_verify_submit_read);
1090 [ - + ]: 9347687 : } else if (rc != 0) {
1091 [ # # ]: 0 : printf("Failed to submit read: %d\n", rc);
1092 : 0 : bdevperf_job_drain(job);
1093 : 0 : g_run_rc = rc;
1094 : 0 : }
1095 : 9347687 : }
1096 : :
1097 : : static void
1098 : 9782587 : bdevperf_verify_write_complete(struct spdk_bdev_io *bdev_io, bool success,
1099 : : void *cb_arg)
1100 : : {
1101 [ + + # # ]: 9782587 : if (success) {
1102 : 9346329 : spdk_bdev_free_io(bdev_io);
1103 : 9346329 : bdevperf_verify_submit_read(cb_arg);
1104 : 897889 : } else {
1105 [ # # ]: 436258 : bdevperf_complete(bdev_io, success, cb_arg);
1106 : : }
1107 : 9782587 : }
1108 : :
1109 : : static void
1110 : 0 : bdevperf_zcopy_populate_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
1111 : : {
1112 [ # # # # ]: 0 : if (!success) {
1113 [ # # ]: 0 : bdevperf_complete(bdev_io, success, cb_arg);
1114 : 0 : return;
1115 : : }
1116 : :
1117 : 0 : spdk_bdev_zcopy_end(bdev_io, false, bdevperf_complete, cb_arg);
1118 : 0 : }
1119 : :
1120 : : static int
1121 : 0 : bdevperf_generate_dif(struct bdevperf_task *task)
1122 : : {
1123 [ # # # # ]: 0 : struct bdevperf_job *job = task->job;
1124 [ # # # # ]: 0 : struct spdk_bdev *bdev = job->bdev;
1125 : 0 : struct spdk_dif_ctx dif_ctx;
1126 : : int rc;
1127 : 0 : struct spdk_dif_ctx_init_ext_opts dif_opts;
1128 : :
1129 : 0 : dif_opts.size = SPDK_SIZEOF(&dif_opts, dif_pi_format);
1130 [ # # ]: 0 : dif_opts.dif_pi_format = spdk_bdev_get_dif_pi_format(bdev);
1131 : 0 : rc = spdk_dif_ctx_init(&dif_ctx,
1132 : 0 : spdk_bdev_get_block_size(bdev),
1133 : 0 : spdk_bdev_get_md_size(bdev),
1134 : 0 : spdk_bdev_is_md_interleaved(bdev),
1135 : 0 : spdk_bdev_is_dif_head_of_md(bdev),
1136 : 0 : spdk_bdev_get_dif_type(bdev),
1137 [ # # # # ]: 0 : job->dif_check_flags,
1138 [ # # # # ]: 0 : task->offset_blocks, 0, 0, 0, 0, &dif_opts);
1139 [ # # ]: 0 : if (rc != 0) {
1140 [ # # # # ]: 0 : fprintf(stderr, "Initialization of DIF context failed\n");
1141 : 0 : return rc;
1142 : : }
1143 : :
1144 [ # # ]: 0 : if (spdk_bdev_is_md_interleaved(bdev)) {
1145 [ # # # # : 0 : rc = spdk_dif_generate(&task->iov, 1, job->io_size_blocks, &dif_ctx);
# # ]
1146 : 0 : } else {
1147 : 0 : struct iovec md_iov = {
1148 [ # # # # ]: 0 : .iov_base = task->md_buf,
1149 [ # # # # ]: 0 : .iov_len = spdk_bdev_get_md_size(bdev) * job->io_size_blocks,
1150 : : };
1151 : :
1152 [ # # # # : 0 : rc = spdk_dix_generate(&task->iov, 1, &md_iov, job->io_size_blocks, &dif_ctx);
# # ]
1153 : : }
1154 : :
1155 [ # # ]: 0 : if (rc != 0) {
1156 [ # # # # ]: 0 : fprintf(stderr, "Generation of DIF/DIX failed\n");
1157 : 0 : }
1158 : :
1159 : 0 : return rc;
1160 : 0 : }
1161 : :
1162 : : static void
1163 : 78195772 : bdevperf_submit_task(void *arg)
1164 : : {
1165 : 78195772 : struct bdevperf_task *task = arg;
1166 [ # # # # ]: 78195772 : struct bdevperf_job *job = task->job;
1167 : : struct spdk_bdev_desc *desc;
1168 : : struct spdk_io_channel *ch;
1169 : : spdk_bdev_io_completion_cb cb_fn;
1170 : : uint64_t offset_in_ios;
1171 : 78195772 : int rc = 0;
1172 : :
1173 [ # # # # ]: 78195772 : desc = job->bdev_desc;
1174 [ # # # # ]: 78195772 : ch = job->ch;
1175 : :
1176 [ + + + + : 78195772 : switch (task->io_type) {
+ - - # #
# # ]
1177 : 16243800 : case SPDK_BDEV_IO_TYPE_WRITE:
1178 [ + + - + : 20583157 : if (spdk_bdev_get_md_size(job->bdev) != 0 && job->dif_check_flags != 0) {
# # # # #
# # # ]
1179 : 0 : rc = bdevperf_generate_dif(task);
1180 : 0 : }
1181 [ + + ]: 20583157 : if (rc == 0) {
1182 [ + + + + : 20582981 : cb_fn = (job->verify || job->reset) ? bdevperf_verify_write_complete : bdevperf_complete;
- + - + #
# # # #
# ]
1183 : :
1184 [ - + - + ]: 20582981 : if (g_zcopy) {
1185 [ # # # # ]: 0 : spdk_bdev_zcopy_end(task->bdev_io, true, cb_fn, task);
1186 : 0 : return;
1187 : : } else {
1188 [ # # ]: 24922162 : rc = spdk_bdev_writev_blocks_with_md(desc, ch, &task->iov, 1,
1189 [ # # # # ]: 4339181 : task->md_buf,
1190 [ # # # # ]: 4339181 : task->offset_blocks,
1191 [ # # # # ]: 4339181 : job->io_size_blocks,
1192 : 4339181 : cb_fn, task);
1193 : : }
1194 : 4339181 : }
1195 : 20583157 : break;
1196 : 1909582 : case SPDK_BDEV_IO_TYPE_FLUSH:
1197 [ # # # # ]: 1909582 : rc = spdk_bdev_flush_blocks(desc, ch, task->offset_blocks,
1198 [ # # # # ]: 0 : job->io_size_blocks, bdevperf_complete, task);
1199 : 1909582 : break;
1200 : 4107965 : case SPDK_BDEV_IO_TYPE_UNMAP:
1201 [ # # # # ]: 4107965 : rc = spdk_bdev_unmap_blocks(desc, ch, task->offset_blocks,
1202 [ # # # # ]: 0 : job->io_size_blocks, bdevperf_complete, task);
1203 : 4107965 : break;
1204 : 1835049 : case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
1205 [ # # # # ]: 2925055 : rc = spdk_bdev_write_zeroes_blocks(desc, ch, task->offset_blocks,
1206 [ # # # # ]: 545003 : job->io_size_blocks, bdevperf_complete, task);
1207 : 2380052 : break;
1208 : 19176742 : case SPDK_BDEV_IO_TYPE_READ:
1209 [ - + - + ]: 49215016 : if (g_zcopy) {
1210 [ # # # # : 0 : rc = spdk_bdev_zcopy_start(desc, ch, NULL, 0, task->offset_blocks, job->io_size_blocks,
# # # # ]
1211 : 0 : true, bdevperf_zcopy_populate_complete, task);
1212 : 0 : } else {
1213 [ # # ]: 79253290 : rc = spdk_bdev_readv_blocks_with_md(desc, ch, &task->iov, 1,
1214 [ # # # # ]: 30038274 : task->md_buf,
1215 [ # # # # ]: 30038274 : task->offset_blocks,
1216 [ # # # # ]: 30038274 : job->io_size_blocks,
1217 : 30038274 : bdevperf_complete, task);
1218 : : }
1219 : 49215016 : break;
1220 : 0 : case SPDK_BDEV_IO_TYPE_ABORT:
1221 [ # # # # ]: 0 : rc = spdk_bdev_abort(desc, ch, task->task_to_abort, bdevperf_abort_complete, task);
1222 : 0 : break;
1223 : 0 : default:
1224 [ # # ]: 0 : assert(false);
1225 : : rc = -EINVAL;
1226 : : break;
1227 : : }
1228 : :
1229 [ + + ]: 78195772 : if (rc == -ENOMEM) {
1230 : 419791 : bdevperf_queue_io_wait_with_cb(task, bdevperf_submit_task);
1231 : 419791 : return;
1232 [ - + ]: 77865841 : } else if (rc != 0) {
1233 [ # # ]: 0 : printf("Failed to submit bdev_io: %d\n", rc);
1234 [ # # # # : 0 : if (job->verify) {
# # # # ]
1235 [ # # # # : 0 : assert(task->offset_blocks / job->io_size_blocks >= job->ios_base);
# # # # #
# # # # #
# # # # ]
1236 [ # # # # : 0 : offset_in_ios = task->offset_blocks / job->io_size_blocks - job->ios_base;
# # # # #
# # # #
# ]
1237 : :
1238 [ # # # # : 0 : assert(spdk_bit_array_get(job->outstanding, offset_in_ios) == true);
# # # # ]
1239 [ # # # # ]: 0 : spdk_bit_array_clear(job->outstanding, offset_in_ios);
1240 : 0 : }
1241 : 0 : bdevperf_job_drain(job);
1242 : 0 : g_run_rc = rc;
1243 : 0 : return;
1244 : : }
1245 : :
1246 [ # # # # ]: 77865841 : job->current_queue_depth++;
1247 : 35012494 : }
1248 : :
1249 : : static void
1250 : 0 : bdevperf_zcopy_get_buf_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
1251 : : {
1252 : 0 : struct bdevperf_task *task = cb_arg;
1253 [ # # # # ]: 0 : struct bdevperf_job *job = task->job;
1254 : 0 : struct iovec *iovs;
1255 : 0 : int iovcnt;
1256 : :
1257 [ # # # # ]: 0 : if (!success) {
1258 : 0 : bdevperf_job_drain(job);
1259 : 0 : g_run_rc = -1;
1260 : 0 : return;
1261 : : }
1262 : :
1263 [ # # # # ]: 0 : task->bdev_io = bdev_io;
1264 [ # # # # ]: 0 : task->io_type = SPDK_BDEV_IO_TYPE_WRITE;
1265 : :
1266 [ # # # # : 0 : if (job->verify || job->reset) {
# # # # #
# # # # #
# # ]
1267 : : /* When job->verify or job->reset is enabled, task->buf is used for
1268 : : * verification of read after write. For write I/O, when zcopy APIs
1269 : : * are used, task->buf cannot be used, and data must be written to
1270 : : * the data buffer allocated underneath bdev layer instead.
1271 : : * Hence we copy task->buf to the allocated data buffer here.
1272 : : */
1273 : 0 : spdk_bdev_io_get_iovec(bdev_io, &iovs, &iovcnt);
1274 [ # # # # ]: 0 : assert(iovcnt == 1);
1275 [ # # # # ]: 0 : assert(iovs != NULL);
1276 : :
1277 [ # # # # : 0 : copy_data(iovs[0].iov_base, iovs[0].iov_len, task->buf, job->buf_size,
# # # # #
# # # # #
# # # # #
# ]
1278 [ # # # # ]: 0 : spdk_bdev_get_block_size(job->bdev),
1279 [ # # # # ]: 0 : spdk_bdev_io_get_md_buf(bdev_io), task->md_buf,
1280 [ # # # # : 0 : spdk_bdev_get_md_size(job->bdev), job->io_size_blocks);
# # # # ]
1281 : 0 : }
1282 : :
1283 : 0 : bdevperf_submit_task(task);
1284 : 0 : }
1285 : :
1286 : : static void
1287 : 0 : bdevperf_prep_zcopy_write_task(void *arg)
1288 : : {
1289 : 0 : struct bdevperf_task *task = arg;
1290 [ # # # # ]: 0 : struct bdevperf_job *job = task->job;
1291 : : int rc;
1292 : :
1293 [ # # # # : 0 : rc = spdk_bdev_zcopy_start(job->bdev_desc, job->ch, NULL, 0,
# # # # ]
1294 [ # # # # : 0 : task->offset_blocks, job->io_size_blocks,
# # # # ]
1295 : 0 : false, bdevperf_zcopy_get_buf_complete, task);
1296 [ # # ]: 0 : if (rc != 0) {
1297 [ # # # # ]: 0 : assert(rc == -ENOMEM);
1298 : 0 : bdevperf_queue_io_wait_with_cb(task, bdevperf_prep_zcopy_write_task);
1299 : 0 : return;
1300 : : }
1301 : :
1302 [ # # # # ]: 0 : job->current_queue_depth++;
1303 : 0 : }
1304 : :
1305 : : static struct bdevperf_task *
1306 : 138187 : bdevperf_job_get_task(struct bdevperf_job *job)
1307 : : {
1308 : : struct bdevperf_task *task;
1309 : :
1310 [ # # # # : 138187 : task = TAILQ_FIRST(&job->task_list);
# # ]
1311 [ + + ]: 138187 : if (!task) {
1312 [ # # ]: 0 : printf("Task allocation failed\n");
1313 [ # # ]: 0 : abort();
1314 : : }
1315 : :
1316 [ + + # # : 138187 : TAILQ_REMOVE(&job->task_list, task, link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
1317 : 138187 : return task;
1318 : : }
1319 : :
1320 : : static void
1321 : 77826175 : bdevperf_submit_single(struct bdevperf_job *job, struct bdevperf_task *task)
1322 : : {
1323 : : uint64_t offset_in_ios;
1324 : : uint64_t rand_value;
1325 : : uint32_t first_clear;
1326 : :
1327 [ + + # # : 77826175 : if (job->zipf) {
# # ]
1328 [ # # # # ]: 5148 : offset_in_ios = spdk_zipf_generate(job->zipf);
1329 [ + + + + : 77826175 : } else if (job->is_random) {
# # # # ]
1330 : : /* RAND_MAX is only INT32_MAX, so use 2 calls to rand_r to
1331 : : * get a large enough value to ensure we are issuing I/O
1332 : : * uniformly across the whole bdev.
1333 : : */
1334 [ # # # # ]: 41737247 : rand_value = (uint64_t)rand_r(&job->seed) * RAND_MAX + rand_r(&job->seed);
1335 [ - + # # : 41737247 : offset_in_ios = rand_value % job->size_in_ios;
# # ]
1336 : :
1337 [ + + - + ]: 41737247 : if (g_random_map) {
1338 : : /* Make sure, that the offset does not exceed the maximum size
1339 : : * of the bit array (verified during job creation)
1340 : : */
1341 [ # # # # ]: 0 : assert(offset_in_ios < UINT32_MAX);
1342 : :
1343 [ # # # # ]: 0 : first_clear = spdk_bit_array_find_first_clear(job->random_map, (uint32_t)offset_in_ios);
1344 : :
1345 [ # # ]: 0 : if (first_clear == UINT32_MAX) {
1346 [ # # # # ]: 0 : first_clear = spdk_bit_array_find_first_clear(job->random_map, 0);
1347 : :
1348 [ # # ]: 0 : if (first_clear == UINT32_MAX) {
1349 : : /* If there are no more clear bits in the array, we start over
1350 : : * and select the previously selected random value.
1351 : : */
1352 [ # # # # ]: 0 : spdk_bit_array_clear_mask(job->random_map);
1353 : 0 : first_clear = (uint32_t)offset_in_ios;
1354 : 0 : }
1355 : 0 : }
1356 : :
1357 [ # # # # ]: 0 : spdk_bit_array_set(job->random_map, first_clear);
1358 : :
1359 : 0 : offset_in_ios = first_clear;
1360 : 0 : }
1361 : 21901369 : } else {
1362 [ # # ]: 36083780 : offset_in_ios = job->offset_in_ios++;
1363 [ + + # # : 36083780 : if (job->offset_in_ios == job->size_in_ios) {
# # # # #
# ]
1364 [ # # # # ]: 5515 : job->offset_in_ios = 0;
1365 : 2085 : }
1366 : :
1367 : : /* Increment of offset_in_ios if there's already an outstanding IO
1368 : : * to that location. We only need this with job->verify as random
1369 : : * offsets are not supported with job->verify at this time.
1370 : : */
1371 [ + + + + : 36083780 : if (job->verify) {
# # # # ]
1372 [ + + # # : 9785086 : assert(spdk_bit_array_find_first_clear(job->outstanding, 0) != UINT32_MAX);
# # # # ]
1373 : :
1374 [ + + # # : 9881748 : while (spdk_bit_array_get(job->outstanding, offset_in_ios)) {
# # ]
1375 [ # # ]: 96662 : offset_in_ios = job->offset_in_ios++;
1376 [ + + # # : 96662 : if (job->offset_in_ios == job->size_in_ios) {
# # # # #
# ]
1377 [ # # # # ]: 1239 : job->offset_in_ios = 0;
1378 : 578 : }
1379 : : }
1380 [ # # # # ]: 9785086 : spdk_bit_array_set(job->outstanding, offset_in_ios);
1381 : 900388 : }
1382 : : }
1383 : :
1384 : : /* For multi-thread to same job, offset_in_ios is relative
1385 : : * to the LBA range assigned for that job. job->offset_blocks
1386 : : * is absolute (entire bdev LBA range).
1387 : : */
1388 [ # # # # : 77826175 : task->offset_blocks = (offset_in_ios + job->ios_base) * job->io_size_blocks;
# # # # #
# # # ]
1389 : :
1390 [ - + + + : 77826175 : if (job->flush) {
# # # # ]
1391 [ # # # # ]: 1909582 : task->io_type = SPDK_BDEV_IO_TYPE_FLUSH;
1392 [ - + + + : 75906297 : } else if (job->unmap) {
# # # # ]
1393 [ # # # # ]: 3952956 : task->io_type = SPDK_BDEV_IO_TYPE_UNMAP;
1394 [ + + + + : 71953341 : } else if (job->write_zeroes) {
# # # # ]
1395 [ # # # # ]: 2380052 : task->io_type = SPDK_BDEV_IO_TYPE_WRITE_ZEROES;
1396 [ + + + + : 73039764 : } else if ((job->rw_percentage == 100) ||
# # # # ]
1397 [ + + + + : 25092326 : (job->rw_percentage != 0 && ((rand_r(&job->seed) % 100) < job->rw_percentage))) {
# # # # #
# # # #
# ]
1398 [ - + - + : 49074108 : assert(!job->verify);
# # # # #
# ]
1399 [ # # # # ]: 49074108 : task->io_type = SPDK_BDEV_IO_TYPE_READ;
1400 [ + + + - ]: 49074108 : if (!g_zcopy) {
1401 [ # # # # : 49073848 : task->iov.iov_base = task->buf;
# # # # #
# ]
1402 [ # # # # : 49073848 : task->iov.iov_len = job->buf_size;
# # # # #
# ]
1403 : 30038154 : }
1404 : 30038414 : } else {
1405 [ + + + + : 20499181 : if (job->verify || job->reset || g_unique_writes) {
+ + + - -
+ + + # #
# # # # #
# ]
1406 [ - + # # : 9785751 : generate_data(job, task->buf, task->md_buf, g_unique_writes);
# # # # #
# ]
1407 : 899635 : }
1408 [ - + - + ]: 20509817 : if (g_zcopy) {
1409 : 0 : bdevperf_prep_zcopy_write_task(task);
1410 : 0 : return;
1411 : : } else {
1412 [ # # # # : 20509817 : task->iov.iov_base = task->buf;
# # # # #
# ]
1413 [ # # # # : 20509817 : task->iov.iov_len = job->buf_size;
# # # # #
# ]
1414 [ # # # # ]: 20509817 : task->io_type = SPDK_BDEV_IO_TYPE_WRITE;
1415 : : }
1416 : : }
1417 : :
1418 : 77826515 : bdevperf_submit_task(task);
1419 : 34928238 : }
1420 : :
1421 : : static int reset_job(void *arg);
1422 : :
1423 : : static void
1424 : 0 : reset_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
1425 : : {
1426 : 0 : struct bdevperf_task *task = cb_arg;
1427 [ # # # # ]: 0 : struct bdevperf_job *job = task->job;
1428 : :
1429 [ # # # # ]: 0 : if (!success) {
1430 [ # # # # ]: 0 : printf("Reset blockdev=%s failed\n", spdk_bdev_get_name(job->bdev));
1431 : 0 : bdevperf_job_drain(job);
1432 : 0 : g_run_rc = -1;
1433 : 0 : }
1434 : :
1435 [ # # # # : 0 : TAILQ_INSERT_TAIL(&job->task_list, task, link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1436 : 0 : spdk_bdev_free_io(bdev_io);
1437 : :
1438 [ # # # # ]: 0 : job->reset_timer = SPDK_POLLER_REGISTER(reset_job, job,
1439 : : 10 * SPDK_SEC_TO_USEC);
1440 : 0 : }
1441 : :
1442 : : static int
1443 : 0 : reset_job(void *arg)
1444 : : {
1445 : 0 : struct bdevperf_job *job = arg;
1446 : : struct bdevperf_task *task;
1447 : : int rc;
1448 : :
1449 [ # # ]: 0 : spdk_poller_unregister(&job->reset_timer);
1450 : :
1451 : : /* Do reset. */
1452 : 0 : task = bdevperf_job_get_task(job);
1453 [ # # # # : 0 : rc = spdk_bdev_reset(job->bdev_desc, job->ch,
# # # # ]
1454 : 0 : reset_cb, task);
1455 [ # # ]: 0 : if (rc) {
1456 [ # # ]: 0 : printf("Reset failed: %d\n", rc);
1457 : 0 : bdevperf_job_drain(job);
1458 : 0 : g_run_rc = -1;
1459 : 0 : }
1460 : :
1461 : 0 : return -1;
1462 : : }
1463 : :
1464 : : static void
1465 : 0 : bdevperf_timeout_cb(void *cb_arg, struct spdk_bdev_io *bdev_io)
1466 : : {
1467 : 0 : struct bdevperf_job *job = cb_arg;
1468 : : struct bdevperf_task *task;
1469 : :
1470 [ # # ]: 0 : job->io_timeout++;
1471 : :
1472 [ # # # # : 0 : if (job->is_draining || !job->abort ||
# # # # #
# # # # #
# # # # ]
1473 [ # # # # ]: 0 : !spdk_bdev_io_type_supported(job->bdev, SPDK_BDEV_IO_TYPE_ABORT)) {
1474 : 0 : return;
1475 : : }
1476 : :
1477 : 0 : task = bdevperf_job_get_task(job);
1478 [ # # ]: 0 : if (task == NULL) {
1479 : 0 : return;
1480 : : }
1481 : :
1482 [ # # # # ]: 0 : task->task_to_abort = spdk_bdev_io_get_cb_arg(bdev_io);
1483 [ # # # # ]: 0 : task->io_type = SPDK_BDEV_IO_TYPE_ABORT;
1484 : :
1485 : 0 : bdevperf_submit_task(task);
1486 : 0 : }
1487 : :
1488 : : static void
1489 : 1098 : bdevperf_job_run(void *ctx)
1490 : : {
1491 : 1098 : struct bdevperf_job *job = ctx;
1492 : : struct bdevperf_task *task;
1493 : : int i;
1494 : :
1495 : : /* Submit initial I/O for this job. Each time one
1496 : : * completes, another will be submitted. */
1497 : :
1498 : : /* Start a timer to stop this I/O chain when the run is over */
1499 [ # # # # ]: 1098 : job->run_timer = SPDK_POLLER_REGISTER(bdevperf_job_drain_timer, job, g_time_in_usec);
1500 [ + + + + : 1098 : if (job->reset) {
# # # # ]
1501 [ # # # # ]: 1 : job->reset_timer = SPDK_POLLER_REGISTER(reset_job, job,
1502 : : 10 * SPDK_SEC_TO_USEC);
1503 : 0 : }
1504 : :
1505 [ + + # # : 139279 : for (i = 0; i < job->queue_depth; i++) {
# # # # ]
1506 : 138181 : task = bdevperf_job_get_task(job);
1507 : 138181 : bdevperf_submit_single(job, task);
1508 : 15464 : }
1509 : 1098 : }
1510 : :
1511 : : static void
1512 : 1640 : _performance_dump_done(void *ctx)
1513 : : {
1514 : 1640 : struct bdevperf_aggregate_stats *aggregate = ctx;
1515 [ # # ]: 1640 : struct bdevperf_stats *stats = &aggregate->total;
1516 : : double average_latency;
1517 : :
1518 [ + + + + ]: 1640 : if (g_summarize_performance) {
1519 [ - + # # : 1520 : printf("%12.2f IOPS, %8.2f MiB/s", stats->total_io_per_second, stats->total_mb_per_second);
# # # # ]
1520 : 1520 : printf("\r");
1521 : 65 : } else {
1522 [ - + ]: 120 : printf("\r =================================================================================="
1523 : : "=================================\n");
1524 [ - + ]: 120 : printf("\r %-28s: %10s %10.2f %10.2f",
1525 [ # # # # : 0 : "Total", "", stats->total_io_per_second, stats->total_mb_per_second);
# # # # ]
1526 [ - + ]: 120 : printf(" %10.2f %8.2f",
1527 [ # # # # : 0 : stats->total_failed_per_second, stats->total_timeout_per_second);
# # # # ]
1528 : :
1529 [ # # # # : 120 : average_latency = ((double)stats->total_tsc / stats->total_io_completed) * SPDK_SEC_TO_USEC /
# # # # ]
1530 : 120 : spdk_get_ticks_hz();
1531 [ - + # # : 120 : printf(" %10.2f %10.2f %10.2f\n", average_latency, stats->min_latency, stats->max_latency);
# # # # ]
1532 : 120 : printf("\n");
1533 : : }
1534 : :
1535 : 1640 : fflush(stdout);
1536 : :
1537 : 1640 : g_performance_dump_active = false;
1538 : :
1539 : 1640 : free(aggregate);
1540 : 1640 : }
1541 : :
1542 : : static void
1543 : 3736 : _performance_dump(void *ctx)
1544 : : {
1545 : 3736 : struct bdevperf_aggregate_stats *stats = ctx;
1546 : 3736 : struct bdevperf_stats job_stats = {0};
1547 [ # # # # ]: 3736 : struct bdevperf_job *job = stats->current_job;
1548 : : uint64_t time_in_usec;
1549 : :
1550 [ + + + + : 3736 : if (job->io_failed > 0 && !job->continue_on_failure) {
- + # # #
# # # #
# ]
1551 [ # # # # ]: 0 : time_in_usec = job->run_time_in_usec;
1552 : 0 : } else {
1553 [ # # # # : 3736 : time_in_usec = stats->total.io_time_in_usec;
# # ]
1554 : : }
1555 : :
1556 : 3736 : bdevperf_job_get_stats(job, &job_stats, time_in_usec, g_show_performance_ema_period);
1557 [ # # ]: 3736 : bdevperf_job_stats_accumulate(&stats->total, &job_stats);
1558 [ + + + + ]: 3736 : if (!g_summarize_performance) {
1559 [ # # # # ]: 120 : performance_dump_job_stdout(stats->current_job, &job_stats);
1560 : 0 : }
1561 : :
1562 : : /* This assumes the jobs list is static after start up time.
1563 : : * That's true right now, but if that ever changed this would need a lock. */
1564 [ # # # # : 3736 : stats->current_job = TAILQ_NEXT(stats->current_job, link);
# # # # #
# # # #
# ]
1565 [ + + # # : 3736 : if (stats->current_job == NULL) {
# # ]
1566 : 1640 : spdk_thread_send_msg(g_main_thread, _performance_dump_done, stats);
1567 : 65 : } else {
1568 [ # # # # : 2096 : spdk_thread_send_msg(stats->current_job->thread, _performance_dump, stats);
# # # # ]
1569 : : }
1570 : 3736 : }
1571 : :
1572 : : static int
1573 : 1666 : performance_statistics_thread(void *arg)
1574 : : {
1575 : : struct bdevperf_aggregate_stats *aggregate;
1576 : : struct bdevperf_stats *stats;
1577 : :
1578 : :
1579 [ + + + + ]: 1666 : if (g_performance_dump_active) {
1580 : 26 : return -1;
1581 : : }
1582 : :
1583 : 1640 : g_performance_dump_active = true;
1584 : :
1585 : 1640 : aggregate = calloc(1, sizeof(*aggregate));
1586 [ + + ]: 1640 : if (aggregate == NULL) {
1587 : 0 : return -1;
1588 : : }
1589 [ # # ]: 1640 : stats = &aggregate->total;
1590 [ # # # # ]: 1640 : stats->min_latency = (double)UINT64_MAX;
1591 : :
1592 : 1640 : g_show_performance_period_num++;
1593 : :
1594 [ # # # # ]: 1640 : stats->io_time_in_usec = g_show_performance_period_num * g_show_performance_period_in_usec;
1595 : :
1596 : : /* Iterate all of the jobs to gather stats
1597 : : * These jobs will not get removed here until a final performance dump is run,
1598 : : * so this should be safe without locking.
1599 : : */
1600 [ # # # # ]: 1640 : aggregate->current_job = TAILQ_FIRST(&g_bdevperf.jobs);
1601 [ - + # # : 1640 : if (aggregate->current_job == NULL) {
# # ]
1602 : 0 : spdk_thread_send_msg(g_main_thread, _performance_dump_done, aggregate);
1603 : 0 : } else {
1604 [ # # # # : 1640 : spdk_thread_send_msg(aggregate->current_job->thread, _performance_dump, aggregate);
# # # # ]
1605 : : }
1606 : :
1607 : 1640 : return -1;
1608 : 65 : }
1609 : :
1610 : : static void
1611 : 349 : bdevperf_test(void)
1612 : : {
1613 : : struct bdevperf_job *job;
1614 : :
1615 [ + + ]: 349 : if (TAILQ_EMPTY(&g_bdevperf.jobs)) {
1616 [ - + ]: 1 : if (g_request) {
1617 : 0 : spdk_jsonrpc_send_error_response_fmt(g_request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
1618 : : "No jobs defined or bdevs created");
1619 : 0 : g_request = NULL;
1620 : 0 : }
1621 : 1 : return;
1622 : : }
1623 : :
1624 [ - + ]: 348 : printf("Running I/O for %" PRIu64 " seconds...\n", g_time_in_usec / (uint64_t)SPDK_SEC_TO_USEC);
1625 : 348 : fflush(stdout);
1626 : :
1627 : : /* Start a timer to dump performance numbers */
1628 : 348 : g_start_tsc = spdk_get_ticks();
1629 [ + + + + ]: 348 : if (!g_summarize_performance) {
1630 [ - + ]: 12 : printf("%*s\n", 107, "Latency(us)");
1631 [ - + ]: 12 : printf("\r %-*s: %10s %10s %10s %10s %8s %10s %10s %10s\n",
1632 : : 28, "Device Information", "runtime(s)", "IOPS", "MiB/s", "Fail/s", "TO/s", "Average", "min", "max");
1633 : 0 : }
1634 [ + - ]: 348 : if (!g_perf_timer) {
1635 : 348 : g_perf_timer = SPDK_POLLER_REGISTER(performance_statistics_thread, NULL,
1636 : : g_show_performance_period_in_usec);
1637 : 15 : }
1638 : :
1639 : : /* Iterate jobs to start all I/O */
1640 [ + + # # : 1446 : TAILQ_FOREACH(job, &g_bdevperf.jobs, link) {
# # # # ]
1641 [ # # # # ]: 1098 : spdk_bdev_set_timeout(job->bdev_desc, g_timeout_in_sec, bdevperf_timeout_cb, job);
1642 : :
1643 : 1098 : g_bdevperf.running_jobs++;
1644 [ # # # # ]: 1098 : spdk_thread_send_msg(job->thread, bdevperf_job_run, job);
1645 : 114 : }
1646 : 15 : }
1647 : :
1648 : : static void
1649 : 90 : _bdevperf_job_drain(void *ctx)
1650 : : {
1651 : 90 : bdevperf_job_drain(ctx);
1652 : 90 : }
1653 : :
1654 : : static void
1655 : 62 : bdevperf_bdev_removed(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx)
1656 : : {
1657 : 62 : struct bdevperf_job *job = event_ctx;
1658 : :
1659 [ + + ]: 62 : if (SPDK_BDEV_EVENT_REMOVE == type) {
1660 [ # # # # ]: 61 : spdk_thread_send_msg(job->thread, _bdevperf_job_drain, job);
1661 : 7 : }
1662 : 62 : }
1663 : :
1664 : : static void
1665 : 1106 : bdevperf_histogram_status_cb(void *cb_arg, int status)
1666 : : {
1667 [ + + ]: 1106 : if (status != 0) {
1668 : 0 : g_run_rc = status;
1669 [ # # # # ]: 0 : if (g_continue_on_failure == false) {
1670 : 0 : g_error_to_exit = true;
1671 : 0 : }
1672 : 0 : }
1673 : :
1674 [ + + ]: 1106 : if (--g_bdev_count == 0) {
1675 [ + - ]: 349 : if (g_run_rc == 0) {
1676 : : /* Ready to run the test */
1677 : 349 : bdevperf_test();
1678 : 15 : } else {
1679 : 0 : bdevperf_test_done(NULL);
1680 : : }
1681 : 15 : }
1682 : 1106 : }
1683 : :
1684 : : static uint32_t g_construct_job_count = 0;
1685 : :
1686 : : static int
1687 : 757 : _bdevperf_enable_histogram(void *ctx, struct spdk_bdev *bdev)
1688 : : {
1689 : 757 : bool *enable = ctx;
1690 : :
1691 : 757 : g_bdev_count++;
1692 : :
1693 [ - + # # ]: 757 : spdk_bdev_histogram_enable(bdev, bdevperf_histogram_status_cb, NULL, *enable);
1694 : :
1695 : 757 : return 0;
1696 : : }
1697 : :
1698 : : static void
1699 : 349 : bdevperf_enable_histogram(bool enable)
1700 : : {
1701 : : struct spdk_bdev *bdev;
1702 : : int rc;
1703 : :
1704 : : /* increment initial g_bdev_count so that it will never reach 0 in the middle of iteration */
1705 : 349 : g_bdev_count = 1;
1706 : :
1707 [ + + ]: 349 : if (g_job_bdev_name != NULL) {
1708 : 41 : bdev = spdk_bdev_get_by_name(g_job_bdev_name);
1709 [ + - ]: 41 : if (bdev) {
1710 : 41 : rc = _bdevperf_enable_histogram(&enable, bdev);
1711 : 0 : } else {
1712 [ # # # # ]: 0 : fprintf(stderr, "Unable to find bdev '%s'\n", g_job_bdev_name);
1713 : 0 : rc = -1;
1714 : : }
1715 : 0 : } else {
1716 : 308 : rc = spdk_for_each_bdev_leaf(&enable, _bdevperf_enable_histogram);
1717 : : }
1718 : :
1719 : 349 : bdevperf_histogram_status_cb(NULL, rc);
1720 : 349 : }
1721 : :
1722 : : static void
1723 : 1447 : _bdevperf_construct_job_done(void *ctx)
1724 : : {
1725 [ + + ]: 1447 : if (--g_construct_job_count == 0) {
1726 [ + + ]: 349 : if (g_run_rc != 0) {
1727 : : /* Something failed. */
1728 : 0 : bdevperf_test_done(NULL);
1729 : 0 : return;
1730 : : }
1731 : :
1732 : : /* always enable histogram. */
1733 : 349 : bdevperf_enable_histogram(true);
1734 [ - + ]: 1113 : } else if (g_run_rc != 0) {
1735 : : /* Reset error as some jobs constructed right */
1736 : 0 : g_run_rc = 0;
1737 [ # # # # ]: 0 : if (g_continue_on_failure == false) {
1738 : 0 : g_error_to_exit = true;
1739 : 0 : }
1740 : 0 : }
1741 : 129 : }
1742 : :
1743 : : /* Checkformat will not allow to use inlined type,
1744 : : this is a workaround */
1745 : : typedef struct spdk_thread *spdk_thread_t;
1746 : :
1747 : : static spdk_thread_t
1748 : 1078 : construct_job_thread(struct spdk_cpuset *cpumask, const char *tag)
1749 : : {
1750 : 492 : struct spdk_cpuset tmp;
1751 : :
1752 : : /* This function runs on the main thread. */
1753 [ + + # # ]: 1078 : assert(g_main_thread == spdk_get_thread());
1754 : :
1755 : : /* Handle default mask */
1756 [ + + ]: 1078 : if (spdk_cpuset_count(cpumask) == 0) {
1757 : 0 : cpumask = &g_all_cpuset;
1758 : 0 : }
1759 : :
1760 : : /* Warn user that mask might need to be changed */
1761 : 1078 : spdk_cpuset_copy(&tmp, cpumask);
1762 : 1078 : spdk_cpuset_or(&tmp, &g_all_cpuset);
1763 [ + + ]: 1078 : if (!spdk_cpuset_equal(&tmp, &g_all_cpuset)) {
1764 [ - + - + ]: 75 : fprintf(stderr, "cpumask for '%s' is too big\n", tag);
1765 : 15 : }
1766 : :
1767 : 1078 : return spdk_thread_create(tag, cpumask);
1768 : : }
1769 : :
1770 : : static uint32_t
1771 : 1003 : _get_next_core(void)
1772 : : {
1773 : : static uint32_t current_core = SPDK_ENV_LCORE_ID_ANY;
1774 : :
1775 [ + + ]: 1003 : if (current_core == SPDK_ENV_LCORE_ID_ANY) {
1776 : 324 : current_core = spdk_env_get_first_core();
1777 : 324 : return current_core;
1778 : : }
1779 : :
1780 : 679 : current_core = spdk_env_get_next_core(current_core);
1781 [ + + ]: 679 : if (current_core == SPDK_ENV_LCORE_ID_ANY) {
1782 : 391 : current_core = spdk_env_get_first_core();
1783 : 48 : }
1784 : :
1785 : 679 : return current_core;
1786 : 95 : }
1787 : :
1788 : : static void
1789 : 1098 : _bdevperf_construct_job(void *ctx)
1790 : : {
1791 : 1098 : struct bdevperf_job *job = ctx;
1792 : :
1793 [ + + - + ]: 1098 : if (g_zcopy) {
1794 [ # # # # : 0 : if (!spdk_bdev_io_type_supported(job->bdev, SPDK_BDEV_IO_TYPE_ZCOPY)) {
# # ]
1795 [ # # ]: 0 : printf("Test requires ZCOPY but bdev module does not support ZCOPY\n");
1796 : 0 : g_run_rc = -ENOTSUP;
1797 : 0 : goto end;
1798 : : }
1799 : 0 : }
1800 : :
1801 [ # # # # : 1098 : job->ch = spdk_bdev_get_io_channel(job->bdev_desc);
# # # # ]
1802 [ + - # # : 1098 : if (!job->ch) {
# # ]
1803 [ # # # # ]: 0 : SPDK_ERRLOG("Could not get io_channel for device %s\n", spdk_bdev_get_name(job->bdev));
1804 : 0 : g_run_rc = -ENOMEM;
1805 : 0 : goto end;
1806 : : }
1807 : :
1808 : 984 : end:
1809 : 1098 : spdk_thread_send_msg(g_main_thread, _bdevperf_construct_job_done, NULL);
1810 : 1098 : }
1811 : :
1812 : : static void
1813 : 1098 : job_init_rw(struct bdevperf_job *job, enum job_config_rw rw)
1814 : : {
1815 [ + + + + : 1098 : switch (rw) {
+ + + + +
+ + + ]
1816 : 43 : case JOB_CONFIG_RW_READ:
1817 [ # # # # ]: 51 : job->rw_percentage = 100;
1818 : 51 : break;
1819 : 25 : case JOB_CONFIG_RW_WRITE:
1820 [ # # # # ]: 28 : job->rw_percentage = 0;
1821 : 28 : break;
1822 : 63 : case JOB_CONFIG_RW_RANDREAD:
1823 [ # # # # ]: 73 : job->is_random = true;
1824 [ # # # # ]: 73 : job->rw_percentage = 100;
1825 [ # # # # ]: 73 : job->seed = rand();
1826 : 73 : break;
1827 : 33 : case JOB_CONFIG_RW_RANDWRITE:
1828 [ # # # # ]: 33 : job->is_random = true;
1829 [ # # # # ]: 33 : job->rw_percentage = 0;
1830 [ # # # # ]: 33 : job->seed = rand();
1831 : 33 : break;
1832 : 32 : case JOB_CONFIG_RW_RW:
1833 [ # # # # ]: 40 : job->is_random = false;
1834 : 40 : break;
1835 : 33 : case JOB_CONFIG_RW_RANDRW:
1836 [ # # # # ]: 33 : job->is_random = true;
1837 [ # # # # ]: 33 : job->seed = rand();
1838 : 33 : break;
1839 : 1 : case JOB_CONFIG_RW_RESET:
1840 : : /* Reset shares the flow with verify. */
1841 [ # # # # ]: 1 : job->reset = true;
1842 : : /* fallthrough */
1843 : 594 : case JOB_CONFIG_RW_VERIFY:
1844 [ # # # # ]: 662 : job->verify = true;
1845 : : /* For verify flow read is done on write completion
1846 : : * callback only, rw_percentage shall not be used. */
1847 [ # # # # ]: 662 : job->rw_percentage = 0;
1848 : 662 : break;
1849 : 37 : case JOB_CONFIG_RW_UNMAP:
1850 [ # # # # ]: 37 : job->unmap = true;
1851 : 37 : break;
1852 : 10 : case JOB_CONFIG_RW_FLUSH:
1853 [ # # # # ]: 10 : job->flush = true;
1854 : 10 : break;
1855 : 114 : case JOB_CONFIG_RW_WRITE_ZEROES:
1856 [ # # # # ]: 131 : job->write_zeroes = true;
1857 : 131 : break;
1858 : : }
1859 : 1098 : }
1860 : :
1861 : : static int
1862 : 1098 : bdevperf_construct_job(struct spdk_bdev *bdev, struct job_config *config,
1863 : : struct spdk_thread *thread)
1864 : : {
1865 : : struct bdevperf_job *job;
1866 : : struct bdevperf_task *task;
1867 : : int block_size, data_block_size;
1868 : : int rc;
1869 : : int task_num, n;
1870 : : int32_t numa_id;
1871 : :
1872 : 1098 : block_size = spdk_bdev_get_block_size(bdev);
1873 : 1098 : data_block_size = spdk_bdev_get_data_block_size(bdev);
1874 : :
1875 : 1098 : job = calloc(1, sizeof(struct bdevperf_job));
1876 [ + + ]: 1098 : if (!job) {
1877 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate memory for new job.\n");
1878 : 0 : return -ENOMEM;
1879 : : }
1880 : :
1881 [ # # # # ]: 1098 : job->thread = thread;
1882 : :
1883 [ - + # # : 1098 : job->name = strdup(spdk_bdev_get_name(bdev));
# # ]
1884 [ + + # # : 1098 : if (!job->name) {
# # ]
1885 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate memory for job name.\n");
1886 : 0 : bdevperf_job_free(job);
1887 : 0 : return -ENOMEM;
1888 : : }
1889 : :
1890 [ # # # # : 1098 : rc = spdk_bdev_open_ext(job->name, true, bdevperf_bdev_removed, job, &job->bdev_desc);
# # ]
1891 [ - + ]: 1098 : if (rc != 0) {
1892 [ # # # # ]: 0 : fprintf(stderr, "Could not open leaf bdev %s, error=%d\n", job->name, rc);
1893 : 0 : bdevperf_job_free(job);
1894 : 0 : return rc;
1895 : : }
1896 : :
1897 [ # # # # : 1098 : job->workload_type = config->rw;
# # # # ]
1898 [ # # # # : 1098 : job->io_size = config->bs;
# # # # ]
1899 [ # # # # : 1098 : job->rw_percentage = config->rwmixread;
# # # # ]
1900 [ - + # # : 1098 : job->continue_on_failure = g_continue_on_failure;
# # ]
1901 [ # # # # : 1098 : job->queue_depth = config->iodepth;
# # # # ]
1902 [ # # # # ]: 1098 : job->bdev = bdev;
1903 [ - + # # : 1098 : job->io_size_blocks = job->io_size / data_block_size;
# # # # #
# ]
1904 [ # # # # : 1098 : job->buf_size = job->io_size_blocks * block_size;
# # # # ]
1905 [ - + # # : 1098 : job->abort = g_abort;
# # ]
1906 [ # # # # ]: 1098 : job_init_rw(job, config->rw);
1907 [ # # # # : 1098 : job->md_check = spdk_bdev_get_dif_type(job->bdev) == SPDK_DIF_DISABLE;
# # # # ]
1908 : :
1909 [ - + - + : 1098 : if ((job->io_size % data_block_size) != 0) {
# # # # ]
1910 [ # # # # ]: 0 : SPDK_ERRLOG("IO size (%d) is not multiples of data block size of bdev %s (%"PRIu32")\n",
1911 : : job->io_size, spdk_bdev_get_name(bdev), data_block_size);
1912 : 0 : bdevperf_job_free(job);
1913 : 0 : return -ENOTSUP;
1914 : : }
1915 : :
1916 [ - + + + : 1098 : if (job->unmap && !spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP)) {
- + # # #
# ]
1917 [ # # ]: 0 : printf("Skipping %s because it does not support unmap\n", spdk_bdev_get_name(bdev));
1918 : 0 : bdevperf_job_free(job);
1919 : 0 : return -ENOTSUP;
1920 : : }
1921 : :
1922 [ + + ]: 1098 : if (spdk_bdev_is_dif_check_enabled(bdev, SPDK_DIF_CHECK_TYPE_REFTAG)) {
1923 [ # # # # : 0 : job->dif_check_flags |= SPDK_DIF_FLAGS_REFTAG_CHECK;
# # ]
1924 : 0 : }
1925 [ + + ]: 1098 : if (spdk_bdev_is_dif_check_enabled(bdev, SPDK_DIF_CHECK_TYPE_GUARD)) {
1926 [ # # # # : 0 : job->dif_check_flags |= SPDK_DIF_FLAGS_GUARD_CHECK;
# # ]
1927 : 0 : }
1928 : :
1929 [ # # # # ]: 1098 : job->offset_in_ios = 0;
1930 : :
1931 [ + + # # : 1098 : if (config->length != 0) {
# # ]
1932 : : /* Use subset of disk */
1933 [ - + # # : 572 : job->size_in_ios = config->length / job->io_size_blocks;
# # # # #
# # # #
# ]
1934 [ - + # # : 572 : job->ios_base = config->offset / job->io_size_blocks;
# # # # #
# # # #
# ]
1935 : 72 : } else {
1936 : : /* Use whole disk */
1937 [ - + # # : 526 : job->size_in_ios = spdk_bdev_get_num_blocks(bdev) / job->io_size_blocks;
# # # # #
# ]
1938 [ # # # # ]: 526 : job->ios_base = 0;
1939 : : }
1940 : :
1941 [ + + + + : 1098 : if (job->is_random && g_zipf_theta > 0) {
- + # # #
# ]
1942 [ # # # # : 0 : job->zipf = spdk_zipf_create(job->size_in_ios, g_zipf_theta, 0);
# # # # ]
1943 : 0 : }
1944 : :
1945 [ + + + + : 1098 : if (job->verify) {
# # # # ]
1946 [ - + # # : 662 : if (job->size_in_ios >= UINT32_MAX) {
# # ]
1947 : 0 : SPDK_ERRLOG("Due to constraints of verify operation, the job storage capacity is too large\n");
1948 : 0 : bdevperf_job_free(job);
1949 : 0 : return -ENOMEM;
1950 : : }
1951 [ # # # # : 662 : job->outstanding = spdk_bit_array_create(job->size_in_ios);
# # # # ]
1952 [ - + # # : 662 : if (job->outstanding == NULL) {
# # ]
1953 : 0 : SPDK_ERRLOG("Could not create outstanding array bitmap for bdev %s\n",
1954 : : spdk_bdev_get_name(bdev));
1955 : 0 : bdevperf_job_free(job);
1956 : 0 : return -ENOMEM;
1957 : : }
1958 [ + + # # : 662 : if (job->queue_depth > (int)job->size_in_ios) {
# # # # #
# ]
1959 [ # # # # : 90 : SPDK_WARNLOG("Due to constraints of verify job, queue depth (-q, %d) can't exceed the number of IO "
# # # # #
# # # # #
# # ]
1960 : : "requests which can be submitted to the bdev %s simultaneously (%"PRIu64"). "
1961 : : "Queue depth is limited to %"PRIu64"\n",
1962 : : job->queue_depth, job->name, job->size_in_ios, job->size_in_ios);
1963 [ # # # # : 90 : job->queue_depth = (int)job->size_in_ios;
# # # # ]
1964 : 18 : }
1965 : 68 : }
1966 : :
1967 [ # # # # ]: 1098 : job->histogram = spdk_histogram_data_alloc();
1968 [ + + # # : 1098 : if (job->histogram == NULL) {
# # ]
1969 [ # # # # ]: 0 : fprintf(stderr, "Failed to allocate histogram\n");
1970 : 0 : bdevperf_job_free(job);
1971 : 0 : return -ENOMEM;
1972 : : }
1973 : :
1974 [ # # # # : 1098 : TAILQ_INIT(&job->task_list);
# # # # #
# # # # #
# # ]
1975 : :
1976 [ + + - + ]: 1098 : if (g_random_map) {
1977 [ # # # # : 0 : if (job->size_in_ios >= UINT32_MAX) {
# # ]
1978 : 0 : SPDK_ERRLOG("Due to constraints of the random map, the job storage capacity is too large\n");
1979 : 0 : bdevperf_job_free(job);
1980 : 0 : return -ENOMEM;
1981 : : }
1982 [ # # # # : 0 : job->random_map = spdk_bit_array_create(job->size_in_ios);
# # # # ]
1983 [ # # # # : 0 : if (job->random_map == NULL) {
# # ]
1984 : 0 : SPDK_ERRLOG("Could not create random_map array bitmap for bdev %s\n",
1985 : : spdk_bdev_get_name(bdev));
1986 : 0 : bdevperf_job_free(job);
1987 : 0 : return -ENOMEM;
1988 : : }
1989 : 0 : }
1990 : :
1991 [ # # # # ]: 1098 : task_num = job->queue_depth;
1992 [ + + + + : 1098 : if (job->reset) {
# # # # ]
1993 [ # # ]: 1 : task_num += 1;
1994 : 0 : }
1995 [ + + - + : 1098 : if (job->abort) {
# # # # ]
1996 [ # # # # : 0 : task_num += job->queue_depth;
# # ]
1997 : 0 : }
1998 : :
1999 [ # # # # : 1098 : TAILQ_INSERT_TAIL(&g_bdevperf.jobs, job, link);
# # # # #
# # # # #
# # # # #
# # # #
# ]
2000 : :
2001 [ # # # # ]: 1098 : numa_id = spdk_bdev_get_numa_id(job->bdev);
2002 : :
2003 [ + + # # ]: 139524 : for (n = 0; n < task_num; n++) {
2004 : 138426 : task = calloc(1, sizeof(struct bdevperf_task));
2005 [ + + ]: 138426 : if (!task) {
2006 [ # # # # ]: 0 : fprintf(stderr, "Failed to allocate task from memory\n");
2007 [ # # ]: 0 : spdk_zipf_free(&job->zipf);
2008 : 0 : return -ENOMEM;
2009 : : }
2010 : :
2011 [ # # # # : 154134 : task->buf = spdk_zmalloc(job->buf_size, spdk_bdev_get_buf_align(job->bdev), NULL,
# # # # #
# # # #
# ]
2012 : 15708 : numa_id, SPDK_MALLOC_DMA);
2013 [ - + # # : 138426 : if (!task->buf) {
# # ]
2014 [ # # # # ]: 0 : fprintf(stderr, "Cannot allocate buf for task=%p\n", task);
2015 [ # # ]: 0 : spdk_zipf_free(&job->zipf);
2016 : 0 : free(task);
2017 : 0 : return -ENOMEM;
2018 : : }
2019 : :
2020 [ + + + + : 138426 : if (job->verify && job->buf_size > SPDK_BDEV_LARGE_BUF_MAX_SIZE) {
- + # # #
# # # #
# ]
2021 [ # # # # : 0 : task->verify_buf = spdk_zmalloc(job->buf_size, spdk_bdev_get_buf_align(job->bdev), NULL,
# # # # #
# # # #
# ]
2022 : 0 : numa_id, SPDK_MALLOC_DMA);
2023 [ # # # # : 0 : if (!task->verify_buf) {
# # ]
2024 [ # # # # ]: 0 : fprintf(stderr, "Cannot allocate buf_verify for task=%p\n", task);
2025 [ # # # # ]: 0 : spdk_free(task->buf);
2026 [ # # ]: 0 : spdk_zipf_free(&job->zipf);
2027 : 0 : free(task);
2028 : 0 : return -ENOMEM;
2029 : : }
2030 : :
2031 [ # # # # : 0 : if (spdk_bdev_is_md_separate(job->bdev)) {
# # ]
2032 [ # # # # : 0 : task->verify_md_buf = spdk_zmalloc(spdk_bdev_get_md_size(bdev) * job->io_size_blocks,
# # # # ]
2033 [ # # # # ]: 0 : spdk_bdev_get_buf_align(job->bdev), NULL, numa_id, SPDK_MALLOC_DMA);
2034 [ # # # # : 0 : if (!task->verify_md_buf) {
# # ]
2035 [ # # # # ]: 0 : fprintf(stderr, "Cannot allocate verify_md_buf for task=%p\n", task);
2036 [ # # # # ]: 0 : spdk_free(task->buf);
2037 [ # # # # ]: 0 : spdk_free(task->verify_buf);
2038 [ # # ]: 0 : spdk_zipf_free(&job->zipf);
2039 : 0 : free(task);
2040 : 0 : return -ENOMEM;
2041 : : }
2042 : 0 : }
2043 : 0 : }
2044 : :
2045 [ + + # # : 138426 : if (spdk_bdev_is_md_separate(job->bdev)) {
# # ]
2046 [ # # # # : 2560 : task->md_buf = spdk_zmalloc(job->io_size_blocks *
# # # # #
# ]
2047 [ # # # # ]: 1280 : spdk_bdev_get_md_size(job->bdev), 0, NULL,
2048 : 0 : numa_id, SPDK_MALLOC_DMA);
2049 [ - + # # : 1280 : if (!task->md_buf) {
# # ]
2050 [ # # # # ]: 0 : fprintf(stderr, "Cannot allocate md buf for task=%p\n", task);
2051 [ # # ]: 0 : spdk_zipf_free(&job->zipf);
2052 [ # # # # ]: 0 : spdk_free(task->verify_buf);
2053 [ # # # # ]: 0 : spdk_free(task->verify_md_buf);
2054 [ # # # # ]: 0 : spdk_free(task->buf);
2055 : 0 : free(task);
2056 : 0 : return -ENOMEM;
2057 : : }
2058 : 0 : }
2059 : :
2060 [ # # # # ]: 138426 : task->job = job;
2061 [ # # # # : 138426 : TAILQ_INSERT_TAIL(&job->task_list, task, link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
2062 : 15708 : }
2063 : :
2064 : 1098 : g_construct_job_count++;
2065 : :
2066 : 1098 : rc = spdk_thread_send_msg(thread, _bdevperf_construct_job, job);
2067 [ + + # # ]: 1098 : assert(rc == 0);
2068 : :
2069 : 1098 : return rc;
2070 : 114 : }
2071 : :
2072 : : static int
2073 : 1108 : parse_rw(const char *str, enum job_config_rw ret)
2074 : : {
2075 [ + + ]: 1108 : if (str == NULL) {
2076 : 75 : return ret;
2077 : : }
2078 : :
2079 [ + + + + : 1033 : if (!strcmp(str, "read")) {
# # ]
2080 : 21 : ret = JOB_CONFIG_RW_READ;
2081 [ + + + + : 1014 : } else if (!strcmp(str, "randread")) {
# # ]
2082 : 73 : ret = JOB_CONFIG_RW_RANDREAD;
2083 [ + + + + : 949 : } else if (!strcmp(str, "write")) {
# # ]
2084 : 28 : ret = JOB_CONFIG_RW_WRITE;
2085 [ + + + + : 914 : } else if (!strcmp(str, "randwrite")) {
# # ]
2086 : 33 : ret = JOB_CONFIG_RW_RANDWRITE;
2087 [ + + + + : 878 : } else if (!strcmp(str, "verify")) {
# # ]
2088 : 661 : ret = JOB_CONFIG_RW_VERIFY;
2089 [ + + + + : 285 : } else if (!strcmp(str, "reset")) {
# # ]
2090 : 1 : ret = JOB_CONFIG_RW_RESET;
2091 [ + + + + : 216 : } else if (!strcmp(str, "unmap")) {
# # ]
2092 : 37 : ret = JOB_CONFIG_RW_UNMAP;
2093 [ + + + + : 179 : } else if (!strcmp(str, "write_zeroes")) {
# # ]
2094 : 131 : ret = JOB_CONFIG_RW_WRITE_ZEROES;
2095 [ + + + + : 65 : } else if (!strcmp(str, "flush")) {
# # ]
2096 : 10 : ret = JOB_CONFIG_RW_FLUSH;
2097 [ + + + + : 38 : } else if (!strcmp(str, "rw")) {
# # ]
2098 : 5 : ret = JOB_CONFIG_RW_RW;
2099 [ - + + - : 34 : } else if (!strcmp(str, "randrw")) {
# # ]
2100 : 33 : ret = JOB_CONFIG_RW_RANDRW;
2101 : 0 : } else {
2102 [ # # ]: 0 : fprintf(stderr, "rw must be one of\n"
2103 : : PATTERN_TYPES_STR "\n");
2104 : 0 : ret = BDEVPERF_CONFIG_ERROR;
2105 : : }
2106 : :
2107 : 1033 : return ret;
2108 : 116 : }
2109 : :
2110 : : static const char *
2111 : 2176 : config_filename_next(const char *filename, char *out)
2112 : : {
2113 : : int i, k;
2114 : :
2115 [ + + ]: 2176 : if (filename == NULL) {
2116 [ # # # # ]: 0 : out[0] = '\0';
2117 : 0 : return NULL;
2118 : : }
2119 : :
2120 [ + + # # : 2176 : if (filename[0] == ':') {
# # ]
2121 [ # # ]: 20 : filename++;
2122 : 4 : }
2123 : :
2124 [ + + ]: 4136 : for (i = 0, k = 0;
2125 [ + + # # : 12116 : filename[i] != '\0' &&
# # ]
2126 [ + + + - : 8803 : filename[i] != ':' &&
# # ]
2127 [ + + + - ]: 18650 : i < BDEVPERF_CONFIG_MAX_FILENAME &&
2128 : 868 : k < (BDEVPERF_CONFIG_MAX_FILENAME - 1);
2129 [ # # ]: 8783 : i++) {
2130 [ + - - + : 8783 : if (filename[i] == ' ' || filename[i] == '\t') {
# # # # #
# # # ]
2131 : 0 : continue;
2132 : : }
2133 : :
2134 [ # # # # : 8783 : out[k++] = filename[i];
# # # # #
# ]
2135 : 868 : }
2136 [ # # # # ]: 2176 : out[k] = 0;
2137 : :
2138 [ # # ]: 2176 : return filename + i;
2139 : 224 : }
2140 : :
2141 : : static struct spdk_thread *
2142 : 0 : get_lcore_thread(uint32_t lcore)
2143 : : {
2144 : : struct lcore_thread *lthread;
2145 : :
2146 [ # # # # : 0 : TAILQ_FOREACH(lthread, &g_lcore_thread_list, link) {
# # # # ]
2147 [ # # # # : 0 : if (lthread->lcore == lcore) {
# # ]
2148 [ # # # # ]: 0 : return lthread->thread;
2149 : : }
2150 : 0 : }
2151 : :
2152 : 0 : return NULL;
2153 : 0 : }
2154 : :
2155 : : static void
2156 : 0 : create_lcore_thread(uint32_t lcore)
2157 : : {
2158 : : struct lcore_thread *lthread;
2159 : 0 : struct spdk_cpuset cpumask = {};
2160 : 0 : char name[32];
2161 : :
2162 : 0 : lthread = calloc(1, sizeof(*lthread));
2163 [ # # # # ]: 0 : assert(lthread != NULL);
2164 : :
2165 [ # # # # ]: 0 : lthread->lcore = lcore;
2166 : :
2167 [ # # ]: 0 : snprintf(name, sizeof(name), "lcore_%u", lcore);
2168 : 0 : spdk_cpuset_set_cpu(&cpumask, lcore, true);
2169 : :
2170 [ # # # # ]: 0 : lthread->thread = spdk_thread_create(name, &cpumask);
2171 [ # # # # : 0 : assert(lthread->thread != NULL);
# # # # ]
2172 : :
2173 [ # # # # : 0 : TAILQ_INSERT_TAIL(&g_lcore_thread_list, lthread, link);
# # # # #
# # # # #
# # # # #
# # # #
# ]
2174 : 0 : }
2175 : :
2176 : : static void
2177 : 349 : bdevperf_construct_jobs(void)
2178 : : {
2179 : 106 : char filename[BDEVPERF_CONFIG_MAX_FILENAME];
2180 : : struct spdk_thread *thread;
2181 : : struct job_config *config;
2182 : : struct spdk_bdev *bdev;
2183 : : const char *filenames;
2184 : : uint32_t i;
2185 : : int rc;
2186 : :
2187 [ + + - + ]: 349 : if (g_one_thread_per_lcore) {
2188 [ # # ]: 0 : SPDK_ENV_FOREACH_CORE(i) {
2189 : 0 : create_lcore_thread(i);
2190 : 0 : }
2191 : 0 : }
2192 : :
2193 [ + + # # : 1427 : TAILQ_FOREACH(config, &job_config_list, link) {
# # # # ]
2194 [ # # # # ]: 1078 : filenames = config->filename;
2195 : :
2196 [ + + + - ]: 1078 : if (!g_one_thread_per_lcore) {
2197 [ # # # # : 1078 : thread = construct_job_thread(&config->cpumask, config->name);
# # ]
2198 : 110 : } else {
2199 [ # # # # ]: 0 : thread = get_lcore_thread(config->lcore);
2200 : : }
2201 [ - + # # ]: 1078 : assert(thread);
2202 : :
2203 [ + + ]: 2176 : while (filenames) {
2204 : 2176 : filenames = config_filename_next(filenames, filename);
2205 [ + + # # ]: 2176 : if (strlen(filename) == 0) {
2206 : 1078 : break;
2207 : : }
2208 : :
2209 : 1098 : bdev = spdk_bdev_get_by_name(filename);
2210 [ + + ]: 1098 : if (!bdev) {
2211 [ # # # # ]: 0 : fprintf(stderr, "Unable to find bdev '%s'\n", filename);
2212 : 0 : g_run_rc = -EINVAL;
2213 : 0 : return;
2214 : : }
2215 : :
2216 : 1098 : rc = bdevperf_construct_job(bdev, config, thread);
2217 [ + + ]: 1098 : if (rc < 0) {
2218 : 0 : g_run_rc = rc;
2219 : 0 : return;
2220 : : }
2221 : : }
2222 : 110 : }
2223 : 15 : }
2224 : :
2225 : : static int
2226 : 1003 : make_cli_job_config(const char *filename, int64_t offset, uint64_t range)
2227 : : {
2228 : 1003 : struct job_config *config = calloc(1, sizeof(*config));
2229 : :
2230 [ + + ]: 1003 : if (config == NULL) {
2231 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate memory for job config\n");
2232 : 0 : return -ENOMEM;
2233 : : }
2234 : :
2235 [ # # # # ]: 1003 : config->name = filename;
2236 [ # # # # ]: 1003 : config->filename = filename;
2237 [ # # # # ]: 1003 : config->lcore = _get_next_core();
2238 [ # # ]: 1003 : spdk_cpuset_zero(&config->cpumask);
2239 [ # # # # : 1003 : spdk_cpuset_set_cpu(&config->cpumask, config->lcore, true);
# # ]
2240 [ # # # # ]: 1003 : config->bs = g_io_size;
2241 [ # # # # ]: 1003 : config->iodepth = g_queue_depth;
2242 [ # # # # ]: 1003 : config->rwmixread = g_rw_percentage;
2243 [ # # # # ]: 1003 : config->offset = offset;
2244 [ # # # # ]: 1003 : config->length = range;
2245 [ # # # # ]: 1003 : config->rw = parse_rw(g_workload_type, BDEVPERF_CONFIG_ERROR);
2246 [ - + # # : 1003 : if ((int)config->rw == BDEVPERF_CONFIG_ERROR) {
# # ]
2247 : 0 : free(config);
2248 : 0 : return -EINVAL;
2249 : : }
2250 : :
2251 [ # # # # : 1003 : TAILQ_INSERT_TAIL(&job_config_list, config, link);
# # # # #
# # # # #
# # # # #
# # # #
# ]
2252 : 1003 : return 0;
2253 : 95 : }
2254 : :
2255 : : static int
2256 : 286 : bdevperf_construct_multithread_job_config(void *ctx, struct spdk_bdev *bdev)
2257 : : {
2258 : 286 : uint32_t *num_cores = ctx;
2259 : : uint32_t i;
2260 : : uint64_t blocks_per_job;
2261 : : int64_t offset;
2262 : : int rc;
2263 : :
2264 [ - + # # ]: 286 : blocks_per_job = spdk_bdev_get_num_blocks(bdev) / *num_cores;
2265 : 286 : offset = 0;
2266 : :
2267 [ + + ]: 858 : SPDK_ENV_FOREACH_CORE(i) {
2268 : 572 : rc = make_cli_job_config(spdk_bdev_get_name(bdev), offset, blocks_per_job);
2269 [ - + ]: 572 : if (rc) {
2270 : 0 : return rc;
2271 : : }
2272 : :
2273 : 572 : offset += blocks_per_job;
2274 : 72 : }
2275 : :
2276 : 286 : return 0;
2277 : 36 : }
2278 : :
2279 : : static void
2280 : 76 : bdevperf_construct_multithread_job_configs(void)
2281 : : {
2282 : : struct spdk_bdev *bdev;
2283 : : uint32_t i;
2284 : 28 : uint32_t num_cores;
2285 : :
2286 : 76 : num_cores = 0;
2287 [ + + ]: 228 : SPDK_ENV_FOREACH_CORE(i) {
2288 : 152 : num_cores++;
2289 : 12 : }
2290 : :
2291 [ + + ]: 76 : if (num_cores == 0) {
2292 : 0 : g_run_rc = -EINVAL;
2293 : 0 : return;
2294 : : }
2295 : :
2296 [ - + ]: 76 : if (g_job_bdev_name != NULL) {
2297 : 0 : bdev = spdk_bdev_get_by_name(g_job_bdev_name);
2298 [ # # ]: 0 : if (!bdev) {
2299 [ # # # # ]: 0 : fprintf(stderr, "Unable to find bdev '%s'\n", g_job_bdev_name);
2300 : 0 : return;
2301 : : }
2302 : 0 : g_run_rc = bdevperf_construct_multithread_job_config(&num_cores, bdev);
2303 : 0 : } else {
2304 : 76 : g_run_rc = spdk_for_each_bdev_leaf(&num_cores, bdevperf_construct_multithread_job_config);
2305 : : }
2306 : :
2307 : 6 : }
2308 : :
2309 : : static int
2310 : 390 : bdevperf_construct_job_config(void *ctx, struct spdk_bdev *bdev)
2311 : : {
2312 : : /* Construct the job */
2313 : 390 : return make_cli_job_config(spdk_bdev_get_name(bdev), 0, 0);
2314 : : }
2315 : :
2316 : : static void
2317 : 349 : bdevperf_construct_job_configs(void)
2318 : : {
2319 : : struct spdk_bdev *bdev;
2320 : :
2321 : : /* There are three different modes for allocating jobs. Standard mode
2322 : : * (the default) creates one spdk_thread per bdev and runs the I/O job there.
2323 : : *
2324 : : * The -C flag places bdevperf into "multithread" mode, meaning it creates
2325 : : * one spdk_thread per bdev PER CORE, and runs a copy of the job on each.
2326 : : * This runs multiple threads per bdev, effectively.
2327 : : *
2328 : : * The -j flag implies "FIO" mode which tries to mimic semantic of FIO jobs.
2329 : : * In "FIO" mode, threads are spawned per-job instead of per-bdev.
2330 : : * Each FIO job can be individually parameterized by filename, cpu mask, etc,
2331 : : * which is different from other modes in that they only support global options.
2332 : : *
2333 : : * Both for standard mode and "multithread" mode, if the -E flag is specified,
2334 : : * it creates one spdk_thread PER CORE. On each core, one spdk_thread is shared by
2335 : : * multiple jobs.
2336 : : */
2337 : :
2338 [ + + ]: 349 : if (g_bdevperf_conf) {
2339 : 20 : goto end;
2340 : : }
2341 : :
2342 [ + + + + ]: 340 : if (g_multithread_mode) {
2343 : 76 : bdevperf_construct_multithread_job_configs();
2344 [ + + ]: 259 : } else if (g_job_bdev_name != NULL) {
2345 : 41 : bdev = spdk_bdev_get_by_name(g_job_bdev_name);
2346 [ + - ]: 41 : if (bdev) {
2347 : : /* Construct the job */
2348 : 41 : g_run_rc = make_cli_job_config(g_job_bdev_name, 0, 0);
2349 : 0 : } else {
2350 [ # # # # ]: 0 : fprintf(stderr, "Unable to find bdev '%s'\n", g_job_bdev_name);
2351 : : }
2352 : 0 : } else {
2353 : 212 : g_run_rc = spdk_for_each_bdev_leaf(NULL, bdevperf_construct_job_config);
2354 : : }
2355 : :
2356 : 334 : end:
2357 : : /* Increment initial construct_jobs count so that it will never reach 0 in the middle
2358 : : * of iteration.
2359 : : */
2360 : 349 : g_construct_job_count = 1;
2361 : :
2362 [ + + ]: 349 : if (g_run_rc == 0) {
2363 : 349 : bdevperf_construct_jobs();
2364 : 15 : }
2365 : :
2366 : 349 : _bdevperf_construct_job_done(NULL);
2367 : 349 : }
2368 : :
2369 : : static int
2370 : 525 : parse_uint_option(struct spdk_conf_section *s, const char *name, int def)
2371 : : {
2372 : : const char *job_name;
2373 : : int tmp;
2374 : :
2375 : 525 : tmp = spdk_conf_section_get_intval(s, name);
2376 [ + + ]: 525 : if (tmp == -1) {
2377 : : /* Field was not found. Check default value
2378 : : * In [global] section it is ok to have undefined values
2379 : : * but for other sections it is not ok */
2380 [ + + ]: 255 : if (def == BDEVPERF_CONFIG_UNDEFINED) {
2381 : 30 : job_name = spdk_conf_section_get_name(s);
2382 [ + + + - : 30 : if (strcmp(job_name, "global") == 0) {
# # ]
2383 : 30 : return def;
2384 : : }
2385 : :
2386 [ # # ]: 0 : fprintf(stderr,
2387 : : "Job '%s' has no '%s' assigned\n",
2388 : 0 : job_name, name);
2389 : 0 : return BDEVPERF_CONFIG_ERROR;
2390 : : }
2391 : 225 : return def;
2392 : : }
2393 : :
2394 : : /* NOTE: get_intval returns nonnegative on success */
2395 [ + + ]: 270 : if (tmp < 0) {
2396 [ # # ]: 0 : fprintf(stderr, "Job '%s' has bad '%s' value.\n",
2397 : 0 : spdk_conf_section_get_name(s), name);
2398 : 0 : return BDEVPERF_CONFIG_ERROR;
2399 : : }
2400 : :
2401 : 270 : return tmp;
2402 : 105 : }
2403 : :
2404 : : /* CLI arguments override parameters for global sections */
2405 : : static void
2406 : 50 : config_set_cli_args(struct job_config *config)
2407 : : {
2408 [ - + ]: 50 : if (g_job_bdev_name) {
2409 [ # # # # ]: 0 : config->filename = g_job_bdev_name;
2410 : 0 : }
2411 [ - + ]: 50 : if (g_io_size > 0) {
2412 [ # # # # ]: 0 : config->bs = g_io_size;
2413 : 0 : }
2414 [ - + ]: 50 : if (g_queue_depth > 0) {
2415 [ # # # # ]: 0 : config->iodepth = g_queue_depth;
2416 : 0 : }
2417 [ - + ]: 50 : if (g_rw_percentage > 0) {
2418 [ # # # # ]: 0 : config->rwmixread = g_rw_percentage;
2419 : 0 : }
2420 [ - + ]: 50 : if (g_workload_type) {
2421 [ # # # # : 0 : config->rw = parse_rw(g_workload_type, config->rw);
# # # # ]
2422 : 0 : }
2423 : 50 : }
2424 : :
2425 : : static int
2426 : 424 : read_job_config(void)
2427 : : {
2428 : 136 : struct job_config global_default_config;
2429 : : struct job_config global_config;
2430 : : struct spdk_conf_section *s;
2431 : 424 : struct job_config *config = NULL;
2432 : : const char *cpumask;
2433 : : const char *rw;
2434 : : bool is_global;
2435 : 424 : int n = 0;
2436 : : int val;
2437 : :
2438 [ + + ]: 424 : if (g_bdevperf_conf_file == NULL) {
2439 : 404 : return 0;
2440 : : }
2441 : :
2442 : 20 : g_bdevperf_conf = spdk_conf_allocate();
2443 [ + + ]: 20 : if (g_bdevperf_conf == NULL) {
2444 [ # # ]: 0 : fprintf(stderr, "Could not allocate job config structure\n");
2445 : 0 : return 1;
2446 : : }
2447 : :
2448 : 20 : spdk_conf_disable_sections_merge(g_bdevperf_conf);
2449 [ - + ]: 20 : if (spdk_conf_read(g_bdevperf_conf, g_bdevperf_conf_file)) {
2450 [ # # ]: 0 : fprintf(stderr, "Invalid job config");
2451 : 0 : return 1;
2452 : : }
2453 : :
2454 : : /* Initialize global defaults */
2455 [ # # ]: 20 : global_default_config.filename = NULL;
2456 : : /* Zero mask is the same as g_all_cpuset
2457 : : * The g_all_cpuset is not initialized yet,
2458 : : * so use zero mask as the default instead */
2459 : 20 : spdk_cpuset_zero(&global_default_config.cpumask);
2460 [ # # ]: 20 : global_default_config.bs = BDEVPERF_CONFIG_UNDEFINED;
2461 [ # # ]: 20 : global_default_config.iodepth = BDEVPERF_CONFIG_UNDEFINED;
2462 : : /* bdevperf has no default for -M option but in FIO the default is 50 */
2463 [ # # ]: 20 : global_default_config.rwmixread = 50;
2464 [ # # ]: 20 : global_default_config.offset = 0;
2465 : : /* length 0 means 100% */
2466 [ # # ]: 20 : global_default_config.length = 0;
2467 [ # # ]: 20 : global_default_config.rw = BDEVPERF_CONFIG_UNDEFINED;
2468 : 20 : config_set_cli_args(&global_default_config);
2469 : :
2470 [ - + # # ]: 20 : if ((int)global_default_config.rw == BDEVPERF_CONFIG_ERROR) {
2471 : 0 : return 1;
2472 : : }
2473 : :
2474 : : /* There is only a single instance of global job_config
2475 : : * We just reset its value when we encounter new [global] section */
2476 : 20 : global_config = global_default_config;
2477 : :
2478 [ + + ]: 41 : for (s = spdk_conf_first_section(g_bdevperf_conf);
2479 [ + + ]: 125 : s != NULL;
2480 : 105 : s = spdk_conf_next_section(s)) {
2481 : 105 : config = calloc(1, sizeof(*config));
2482 [ + + ]: 105 : if (config == NULL) {
2483 [ # # ]: 0 : fprintf(stderr, "Unable to allocate memory for job config\n");
2484 : 0 : return 1;
2485 : : }
2486 : :
2487 [ # # # # ]: 105 : config->name = spdk_conf_section_get_name(s);
2488 [ - + # # : 105 : is_global = strcmp(config->name, "global") == 0;
# # # # ]
2489 : :
2490 [ + + # # ]: 105 : if (is_global) {
2491 : 30 : global_config = global_default_config;
2492 : 6 : }
2493 : :
2494 [ # # # # ]: 105 : config->filename = spdk_conf_section_get_val(s, "filename");
2495 [ + + # # : 105 : if (config->filename == NULL) {
# # ]
2496 [ # # # # : 60 : config->filename = global_config.filename;
# # ]
2497 : 12 : }
2498 [ + + # # ]: 105 : if (!is_global) {
2499 [ + + # # : 75 : if (config->filename == NULL) {
# # ]
2500 [ # # # # ]: 0 : fprintf(stderr, "Job '%s' expects 'filename' parameter\n", config->name);
2501 : 0 : goto error;
2502 [ + + + + : 90 : } else if (strnlen(config->filename, BDEVPERF_CONFIG_MAX_FILENAME)
# # # # #
# ]
2503 : 15 : >= BDEVPERF_CONFIG_MAX_FILENAME) {
2504 [ # # ]: 0 : fprintf(stderr,
2505 : : "filename for '%s' job is too long. Max length is %d\n",
2506 [ # # # # ]: 0 : config->name, BDEVPERF_CONFIG_MAX_FILENAME);
2507 : 0 : goto error;
2508 : : }
2509 : 15 : }
2510 : :
2511 : 105 : cpumask = spdk_conf_section_get_val(s, "cpumask");
2512 [ + + ]: 105 : if (cpumask == NULL) {
2513 [ # # ]: 15 : config->cpumask = global_config.cpumask;
2514 [ + + # # ]: 93 : } else if (spdk_cpuset_parse(&config->cpumask, cpumask)) {
2515 [ # # # # ]: 0 : fprintf(stderr, "Job '%s' has bad 'cpumask' value\n", config->name);
2516 : 0 : goto error;
2517 : : }
2518 : :
2519 [ # # # # : 105 : config->bs = parse_uint_option(s, "bs", global_config.bs);
# # ]
2520 [ + + # # : 105 : if (config->bs == BDEVPERF_CONFIG_ERROR) {
# # ]
2521 : 0 : goto error;
2522 [ + + # # : 105 : } else if (config->bs == 0) {
# # ]
2523 [ # # # # ]: 0 : fprintf(stderr, "'bs' of job '%s' must be greater than 0\n", config->name);
2524 : 0 : goto error;
2525 : : }
2526 : :
2527 [ # # # # : 105 : config->iodepth = parse_uint_option(s, "iodepth", global_config.iodepth);
# # ]
2528 [ + + # # : 105 : if (config->iodepth == BDEVPERF_CONFIG_ERROR) {
# # ]
2529 : 0 : goto error;
2530 [ + + # # : 105 : } else if (config->iodepth == 0) {
# # ]
2531 [ # # ]: 0 : fprintf(stderr,
2532 : : "'iodepth' of job '%s' must be greater than 0\n",
2533 [ # # # # ]: 0 : config->name);
2534 : 0 : goto error;
2535 : : }
2536 : :
2537 [ # # # # : 105 : config->rwmixread = parse_uint_option(s, "rwmixread", global_config.rwmixread);
# # ]
2538 [ + + # # : 105 : if (config->rwmixread == BDEVPERF_CONFIG_ERROR) {
# # ]
2539 : 0 : goto error;
2540 [ + + # # : 105 : } else if (config->rwmixread > 100) {
# # ]
2541 [ # # ]: 0 : fprintf(stderr,
2542 : : "'rwmixread' value of '%s' job is not in 0-100 range\n",
2543 [ # # # # ]: 0 : config->name);
2544 : 0 : goto error;
2545 : : }
2546 : :
2547 [ # # # # : 105 : config->offset = parse_uint_option(s, "offset", global_config.offset);
# # ]
2548 [ + + # # : 105 : if (config->offset == BDEVPERF_CONFIG_ERROR) {
# # ]
2549 : 0 : goto error;
2550 : : }
2551 : :
2552 [ # # ]: 105 : val = parse_uint_option(s, "length", global_config.length);
2553 [ + + ]: 105 : if (val == BDEVPERF_CONFIG_ERROR) {
2554 : 0 : goto error;
2555 : : }
2556 [ # # # # ]: 105 : config->length = val;
2557 : :
2558 : 105 : rw = spdk_conf_section_get_val(s, "rw");
2559 [ # # # # : 105 : config->rw = parse_rw(rw, global_config.rw);
# # ]
2560 [ + + # # : 105 : if ((int)config->rw == BDEVPERF_CONFIG_ERROR) {
# # ]
2561 [ # # # # ]: 0 : fprintf(stderr, "Job '%s' has bad 'rw' value\n", config->name);
2562 : 0 : goto error;
2563 [ + + + + : 105 : } else if (!is_global && (int)config->rw == BDEVPERF_CONFIG_UNDEFINED) {
# # # # #
# ]
2564 [ # # # # ]: 0 : fprintf(stderr, "Job '%s' has no 'rw' assigned\n", config->name);
2565 : 0 : goto error;
2566 : : }
2567 : :
2568 [ + + # # ]: 105 : if (is_global) {
2569 : 30 : config_set_cli_args(config);
2570 : 30 : global_config = *config;
2571 : 30 : free(config);
2572 : 30 : config = NULL;
2573 : 6 : } else {
2574 [ # # # # : 75 : TAILQ_INSERT_TAIL(&job_config_list, config, link);
# # # # #
# # # # #
# # # # #
# # # #
# ]
2575 [ # # ]: 75 : n++;
2576 : : }
2577 : 21 : }
2578 : :
2579 [ + + ]: 20 : if (g_rpc_log_file_name != NULL) {
2580 : 0 : g_rpc_log_file = fopen(g_rpc_log_file_name, "a");
2581 [ # # ]: 0 : if (g_rpc_log_file == NULL) {
2582 [ # # ]: 0 : fprintf(stderr, "Failed to open %s\n", g_rpc_log_file_name);
2583 : 0 : goto error;
2584 : : }
2585 : 0 : }
2586 : :
2587 : 20 : printf("Using job config with %d jobs\n", n);
2588 : 20 : return 0;
2589 : 0 : error:
2590 : 0 : free(config);
2591 : 0 : return 1;
2592 : 19 : }
2593 : :
2594 : : static void
2595 : 376 : bdevperf_run(void *arg1)
2596 : : {
2597 : : uint32_t i;
2598 : :
2599 : 376 : g_main_thread = spdk_get_thread();
2600 : :
2601 : 376 : spdk_cpuset_zero(&g_all_cpuset);
2602 [ + + ]: 831 : SPDK_ENV_FOREACH_CORE(i) {
2603 : 455 : spdk_cpuset_set_cpu(&g_all_cpuset, i, true);
2604 : 21 : }
2605 : :
2606 [ + + + + ]: 376 : if (g_wait_for_tests) {
2607 : : /* Do not perform any tests until RPC is received */
2608 : 197 : return;
2609 : : }
2610 : :
2611 : 179 : bdevperf_construct_job_configs();
2612 : 15 : }
2613 : :
2614 : : static void
2615 : 162 : rpc_perform_tests_reset(void)
2616 : : {
2617 : : /* Reset g_run_rc to 0 for the next test run. */
2618 : 162 : g_run_rc = 0;
2619 : :
2620 : : /* Reset g_stats to 0 for the next test run. */
2621 [ - + ]: 162 : memset(&g_stats, 0, sizeof(g_stats));
2622 : :
2623 : : /* Reset g_show_performance_period_num to 0 for the next test run. */
2624 : 162 : g_show_performance_period_num = 0;
2625 : 162 : }
2626 : :
2627 : : static void
2628 : 162 : rpc_perform_tests_cb(void)
2629 : : {
2630 : 162 : struct spdk_jsonrpc_request *request = g_request;
2631 : :
2632 : 162 : g_request = NULL;
2633 : :
2634 [ + + ]: 162 : if (g_run_rc) {
2635 : 6 : spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
2636 [ # # ]: 1 : "bdevperf failed with error %s", spdk_strerror(-g_run_rc));
2637 : 1 : }
2638 : :
2639 : 162 : rpc_perform_tests_reset();
2640 : 162 : }
2641 : :
2642 : : struct rpc_bdevperf_params {
2643 : : int time_in_sec;
2644 : : char *workload_type;
2645 : : int queue_depth;
2646 : : char *io_size;
2647 : : int rw_percentage;
2648 : : };
2649 : :
2650 : : static const struct spdk_json_object_decoder rpc_bdevperf_params_decoders[] = {
2651 : : {"time_in_sec", offsetof(struct rpc_bdevperf_params, time_in_sec), spdk_json_decode_int32, true},
2652 : : {"workload_type", offsetof(struct rpc_bdevperf_params, workload_type), spdk_json_decode_string, true},
2653 : : {"queue_depth", offsetof(struct rpc_bdevperf_params, queue_depth), spdk_json_decode_int32, true},
2654 : : {"io_size", offsetof(struct rpc_bdevperf_params, io_size), spdk_json_decode_string, true},
2655 : : {"rw_percentage", offsetof(struct rpc_bdevperf_params, rw_percentage), spdk_json_decode_int32, true},
2656 : : };
2657 : :
2658 : : static void
2659 : 3 : rpc_apply_bdevperf_params(struct rpc_bdevperf_params *params)
2660 : : {
2661 [ + - # # : 3 : if (params->workload_type) {
# # ]
2662 : : /* we need to clear previously settled parameter to avoid memory leak */
2663 : 3 : free(g_workload_type);
2664 [ - + # # : 3 : g_workload_type = strdup(params->workload_type);
# # ]
2665 : 0 : }
2666 [ + - # # : 3 : if (params->queue_depth) {
# # ]
2667 [ # # # # ]: 3 : g_queue_depth = params->queue_depth;
2668 : 0 : }
2669 [ + - # # : 3 : if (params->io_size) {
# # ]
2670 [ # # # # ]: 3 : bdevperf_parse_arg('o', params->io_size);
2671 : 0 : }
2672 [ + - # # : 3 : if (params->time_in_sec) {
# # ]
2673 [ # # # # ]: 3 : g_time_in_sec = params->time_in_sec;
2674 : 0 : }
2675 [ - + # # : 3 : if (params->rw_percentage) {
# # ]
2676 [ # # # # ]: 0 : g_rw_percentage = params->rw_percentage;
2677 : 0 : g_mix_specified = true;
2678 : 0 : } else {
2679 : 3 : g_mix_specified = false;
2680 : : }
2681 : 3 : }
2682 : :
2683 : : static void
2684 : 170 : rpc_perform_tests(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
2685 : : {
2686 : 170 : struct rpc_bdevperf_params req = {}, backup = {};
2687 : : int rc;
2688 : :
2689 [ - + ]: 170 : if (g_request != NULL) {
2690 [ # # # # ]: 0 : fprintf(stderr, "Another test is already in progress.\n");
2691 : 0 : spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
2692 : 0 : spdk_strerror(-EINPROGRESS));
2693 : 0 : return;
2694 : : }
2695 : :
2696 [ + + ]: 170 : if (params) {
2697 [ - + ]: 3 : if (spdk_json_decode_object_relaxed(params, rpc_bdevperf_params_decoders,
2698 : : SPDK_COUNTOF(rpc_bdevperf_params_decoders),
2699 : : &req)) {
2700 : 0 : spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_PARSE_ERROR,
2701 : : "spdk_json_decode_object failed");
2702 : 0 : return;
2703 : : }
2704 : :
2705 [ + + ]: 3 : if (g_workload_type) {
2706 [ - + # # ]: 2 : backup.workload_type = strdup(g_workload_type);
2707 : 0 : }
2708 [ # # ]: 3 : backup.queue_depth = g_queue_depth;
2709 [ - + ]: 3 : if (asprintf(&backup.io_size, "%d", g_io_size) < 0) {
2710 [ # # # # ]: 0 : fprintf(stderr, "Couldn't allocate memory for queue depth");
2711 : 0 : goto rpc_error;
2712 : : }
2713 : 3 : backup.time_in_sec = g_time_in_sec;
2714 [ # # ]: 3 : backup.rw_percentage = g_rw_percentage;
2715 : :
2716 : 3 : rpc_apply_bdevperf_params(&req);
2717 : :
2718 [ # # ]: 3 : free(req.workload_type);
2719 [ # # ]: 3 : free(req.io_size);
2720 : 0 : }
2721 : :
2722 : 170 : rc = verify_test_params();
2723 : :
2724 [ - + ]: 170 : if (rc) {
2725 : 0 : spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_PARSE_ERROR,
2726 : : "Invalid parameters provided");
2727 : : /* restore old params on error */
2728 : 0 : rpc_apply_bdevperf_params(&backup);
2729 : 0 : goto rpc_error;
2730 : : }
2731 : :
2732 : 170 : g_request = request;
2733 : :
2734 : : /* Only construct job configs at the first test run. */
2735 [ + - ]: 175 : if (TAILQ_EMPTY(&job_config_list)) {
2736 : 170 : bdevperf_construct_job_configs();
2737 : 5 : } else {
2738 : 0 : bdevperf_construct_jobs();
2739 : : }
2740 : :
2741 : 165 : rpc_error:
2742 [ # # ]: 170 : free(backup.io_size);
2743 [ # # ]: 170 : free(backup.workload_type);
2744 : 5 : }
2745 : 424 : SPDK_RPC_REGISTER("perform_tests", rpc_perform_tests, SPDK_RPC_RUNTIME)
2746 : :
2747 : : static void
2748 : 196 : spdk_bdevperf_shutdown_cb(void)
2749 : : {
2750 : 196 : g_shutdown = true;
2751 : : struct bdevperf_job *job, *tmp;
2752 : :
2753 [ + + # # ]: 196 : if (g_bdevperf.running_jobs == 0) {
2754 : 185 : bdevperf_test_done(NULL);
2755 : 185 : return;
2756 : : }
2757 : :
2758 : : /* Iterate jobs to stop all I/O */
2759 [ + + # # : 40 : TAILQ_FOREACH_SAFE(job, &g_bdevperf.jobs, link, tmp) {
# # # # #
# ]
2760 [ # # # # ]: 29 : spdk_thread_send_msg(job->thread, _bdevperf_job_drain, job);
2761 : 0 : }
2762 : 4 : }
2763 : :
2764 : : static int
2765 : 2097 : bdevperf_parse_arg(int ch, char *arg)
2766 : : {
2767 : : long long tmp;
2768 : :
2769 [ + + ]: 2097 : if (ch == 'w') {
2770 [ - + ]: 403 : g_workload_type = strdup(arg);
2771 [ + + ]: 1709 : } else if (ch == 'T') {
2772 : 50 : g_job_bdev_name = arg;
2773 [ + + ]: 1644 : } else if (ch == 'z') {
2774 : 197 : g_wait_for_tests = true;
2775 [ - + ]: 1452 : } else if (ch == 'Z') {
2776 : 0 : g_zcopy = true;
2777 [ - + ]: 1447 : } else if (ch == 'X') {
2778 : 0 : g_abort = true;
2779 [ + + ]: 1447 : } else if (ch == 'C') {
2780 : 81 : g_multithread_mode = true;
2781 [ + + ]: 1373 : } else if (ch == 'f') {
2782 : 42 : g_continue_on_failure = true;
2783 [ + + ]: 1325 : } else if (ch == 'j') {
2784 : 20 : g_bdevperf_conf_file = arg;
2785 [ - + ]: 1308 : } else if (ch == 'F') {
2786 : 0 : char *endptr;
2787 : :
2788 [ # # ]: 0 : errno = 0;
2789 [ # # ]: 0 : g_zipf_theta = strtod(arg, &endptr);
2790 [ # # # # : 0 : if (errno || arg == endptr || g_zipf_theta < 0) {
# # # # ]
2791 [ # # ]: 0 : fprintf(stderr, "Illegal zipf theta value %s\n", arg);
2792 : 0 : return -EINVAL;
2793 : : }
2794 [ - + ]: 1304 : } else if (ch == 'l') {
2795 : 0 : g_latency_display_level++;
2796 [ - + ]: 1304 : } else if (ch == 'D') {
2797 : 0 : g_random_map = true;
2798 [ - + ]: 1304 : } else if (ch == 'E') {
2799 : 0 : g_one_thread_per_lcore = true;
2800 [ - + ]: 1304 : } else if (ch == 'J') {
2801 : 0 : g_rpc_log_file_name = arg;
2802 [ + + ]: 1304 : } else if (ch == 'o') {
2803 : 126 : uint64_t size;
2804 : :
2805 [ - + ]: 406 : if (spdk_parse_capacity(arg, &size, NULL) != 0) {
2806 [ # # ]: 0 : fprintf(stderr, "Invalid IO size: %s\n", arg);
2807 : 0 : return -EINVAL;
2808 : : }
2809 : 406 : g_io_size = (int)size;
2810 [ + + ]: 913 : } else if (ch == 'U') {
2811 : 15 : g_unique_writes = true;
2812 : 0 : } else {
2813 : 883 : tmp = spdk_strtoll(arg, 10);
2814 [ + + ]: 883 : if (tmp < 0) {
2815 [ # # ]: 0 : fprintf(stderr, "Parse failed for the option %c.\n", ch);
2816 : 0 : return tmp;
2817 [ - + ]: 883 : } else if (tmp >= INT_MAX) {
2818 [ # # ]: 0 : fprintf(stderr, "Parsed option was too large %c.\n", ch);
2819 : 0 : return -ERANGE;
2820 : : }
2821 : :
2822 [ + + - + : 883 : switch (ch) {
- + - ]
2823 : 388 : case 'q':
2824 : 403 : g_queue_depth = tmp;
2825 : 403 : break;
2826 : 404 : case 't':
2827 : 423 : g_time_in_sec = tmp;
2828 : 423 : break;
2829 : 0 : case 'k':
2830 : 0 : g_timeout_in_sec = tmp;
2831 : 0 : break;
2832 : 45 : case 'M':
2833 : 45 : g_rw_percentage = tmp;
2834 : 45 : g_mix_specified = true;
2835 : 45 : break;
2836 : 0 : case 'P':
2837 : 0 : g_show_performance_ema_period = tmp;
2838 : 0 : break;
2839 : 12 : case 'S':
2840 : 12 : g_summarize_performance = false;
2841 : 12 : g_show_performance_period_in_usec = tmp * SPDK_SEC_TO_USEC;
2842 : 12 : break;
2843 : 0 : default:
2844 : 0 : return -EINVAL;
2845 : : }
2846 : : }
2847 : 2097 : return 0;
2848 : 81 : }
2849 : :
2850 : : static void
2851 : 0 : bdevperf_usage(void)
2852 : : {
2853 [ # # ]: 0 : printf(" -q <depth> io depth\n");
2854 [ # # ]: 0 : printf(" -o <size> io size in bytes\n");
2855 [ # # ]: 0 : printf(" -w <type> io pattern type, must be one of " PATTERN_TYPES_STR "\n");
2856 [ # # ]: 0 : printf(" -t <time> time in seconds\n");
2857 [ # # ]: 0 : printf(" -k <timeout> timeout in seconds to detect starved I/O (default is 0 and disabled)\n");
2858 [ # # ]: 0 : printf(" -M <percent> rwmixread (100 for reads, 0 for writes)\n");
2859 [ # # ]: 0 : printf(" -P <num> number of moving average period\n");
2860 [ # # ]: 0 : printf("\t\t(If set to n, show weighted mean of the previous n IO/s in real time)\n");
2861 [ # # ]: 0 : printf("\t\t(Formula: M = 2 / (n + 1), EMA[i+1] = IO/s * M + (1 - M) * EMA[i])\n");
2862 [ # # ]: 0 : printf("\t\t(only valid with -S)\n");
2863 [ # # ]: 0 : printf(" -S <period> show performance result in real time every <period> seconds\n");
2864 [ # # ]: 0 : printf(" -T <bdev> bdev to run against. Default: all available bdevs.\n");
2865 [ # # ]: 0 : printf(" -f continue processing I/O even after failures\n");
2866 [ # # ]: 0 : printf(" -F <zipf theta> use zipf distribution for random I/O\n");
2867 [ # # ]: 0 : printf(" -Z enable using zcopy bdev API for read or write I/O\n");
2868 [ # # ]: 0 : printf(" -z start bdevperf, but wait for perform_tests RPC to start tests\n");
2869 [ # # ]: 0 : printf(" (See examples/bdev/bdevperf/bdevperf.py)\n");
2870 [ # # ]: 0 : printf(" -X abort timed out I/O\n");
2871 [ # # ]: 0 : printf(" -C enable every core to send I/Os to each bdev\n");
2872 [ # # ]: 0 : printf(" -j <filename> use job config file\n");
2873 [ # # ]: 0 : printf(" -l display latency histogram, default: disable. -l display summary, -ll display details\n");
2874 [ # # ]: 0 : printf(" -D use a random map for picking offsets not previously read or written (for all jobs)\n");
2875 [ # # ]: 0 : printf(" -E share per lcore thread among jobs. Available only if -j is not used.\n");
2876 [ # # ]: 0 : printf(" -J File name to open with append mode and log JSON RPC calls.\n");
2877 [ # # ]: 0 : printf(" -U generate unique data for each write I/O, has no effect on non-write I/O\n");
2878 : 0 : }
2879 : :
2880 : : static void
2881 : 424 : bdevperf_fini(void)
2882 : : {
2883 : 424 : free_job_config();
2884 : 424 : free(g_workload_type);
2885 : :
2886 [ - + ]: 424 : if (g_rpc_log_file != NULL) {
2887 [ # # ]: 0 : fclose(g_rpc_log_file);
2888 : 0 : g_rpc_log_file = NULL;
2889 : 0 : }
2890 : 424 : }
2891 : :
2892 : : static int
2893 : 594 : verify_test_params(void)
2894 : : {
2895 [ + + + + ]: 594 : if (!g_bdevperf_conf_file && g_queue_depth <= 0) {
2896 : 1 : goto out;
2897 : : }
2898 [ + + + + ]: 593 : if (!g_bdevperf_conf_file && g_io_size <= 0) {
2899 : 0 : goto out;
2900 : : }
2901 [ + + + + ]: 593 : if (!g_bdevperf_conf_file && !g_workload_type) {
2902 : 0 : goto out;
2903 : : }
2904 [ + + + + : 593 : if (g_bdevperf_conf_file && g_one_thread_per_lcore) {
- + ]
2905 : 0 : printf("If bdevperf's config file is used, per lcore thread cannot be used\n");
2906 : 0 : goto out;
2907 : : }
2908 [ - + ]: 593 : if (g_time_in_sec <= 0) {
2909 : 0 : goto out;
2910 : : }
2911 : 593 : g_time_in_usec = g_time_in_sec * SPDK_SEC_TO_USEC;
2912 : :
2913 [ - + ]: 593 : if (g_timeout_in_sec < 0) {
2914 : 0 : goto out;
2915 : : }
2916 : :
2917 [ - + - + : 593 : if (g_abort && !g_timeout_in_sec) {
- - ]
2918 : 0 : printf("Timeout must be set for abort option, Ignoring g_abort\n");
2919 : 0 : }
2920 : :
2921 [ - + - - : 593 : if (g_show_performance_ema_period > 0 && g_summarize_performance) {
- - ]
2922 [ # # ]: 0 : fprintf(stderr, "-P option must be specified with -S option\n");
2923 : 0 : return 1;
2924 : : }
2925 : :
2926 [ + + ]: 593 : if (g_io_size > SPDK_BDEV_LARGE_BUF_MAX_SIZE) {
2927 : 84 : printf("I/O size of %d is greater than zero copy threshold (%d).\n",
2928 : 0 : g_io_size, SPDK_BDEV_LARGE_BUF_MAX_SIZE);
2929 : 84 : printf("Zero copy mechanism will not be used.\n");
2930 : 84 : g_zcopy = false;
2931 : 0 : }
2932 : :
2933 [ + + ]: 593 : if (g_bdevperf_conf_file) {
2934 : : /* workload_type verification happens during config file parsing */
2935 : 20 : return 0;
2936 : : }
2937 : :
2938 [ + + + + : 573 : if (!strcmp(g_workload_type, "verify") ||
# # # # ]
2939 [ - + + + ]: 367 : !strcmp(g_workload_type, "reset")) {
2940 : 207 : g_rw_percentage = 50;
2941 : 207 : g_verify = true;
2942 [ + + + + : 207 : if (!strcmp(g_workload_type, "reset")) {
# # ]
2943 : 1 : g_reset = true;
2944 : 0 : }
2945 : 4 : }
2946 : :
2947 [ + + + + : 573 : if (!strcmp(g_workload_type, "read") ||
# # # # ]
2948 [ + + + + : 559 : !strcmp(g_workload_type, "randread") ||
# # ]
2949 [ + + + + : 469 : !strcmp(g_workload_type, "write") ||
# # ]
2950 [ + + + + : 455 : !strcmp(g_workload_type, "randwrite") ||
# # ]
2951 [ + + + + : 397 : !strcmp(g_workload_type, "verify") ||
# # ]
2952 [ + + + + : 191 : !strcmp(g_workload_type, "reset") ||
# # ]
2953 [ + + + + : 190 : !strcmp(g_workload_type, "unmap") ||
# # ]
2954 [ - + + + : 154 : !strcmp(g_workload_type, "write_zeroes") ||
# # ]
2955 [ - + + + ]: 80 : !strcmp(g_workload_type, "flush")) {
2956 [ + + - + ]: 502 : if (g_mix_specified) {
2957 [ # # ]: 0 : fprintf(stderr, "Ignoring -M option... Please use -M option"
2958 : : " only when using rw or randrw.\n");
2959 : 0 : }
2960 : 20 : }
2961 : :
2962 [ + + + - : 573 : if (!strcmp(g_workload_type, "rw") ||
# # # # ]
2963 [ - + + + ]: 573 : !strcmp(g_workload_type, "randrw")) {
2964 [ + - - + ]: 71 : if (g_rw_percentage < 0 || g_rw_percentage > 100) {
2965 [ # # ]: 0 : fprintf(stderr,
2966 : : "-M must be specified to value from 0 to 100 "
2967 : : "for rw or randrw.\n");
2968 : 0 : return 1;
2969 : : }
2970 : 0 : }
2971 : :
2972 [ + + + + : 583 : if (strcmp(g_workload_type, "randread") &&
# # # # ]
2973 [ + + + + : 483 : strcmp(g_workload_type, "randwrite") &&
# # ]
2974 [ + + + + ]: 425 : strcmp(g_workload_type, "randrw")) {
2975 [ - + - + ]: 354 : if (g_random_map) {
2976 [ # # ]: 0 : fprintf(stderr, "Ignoring -D option... Please use -D option"
2977 : : " only when using randread, randwrite or randrw.\n");
2978 : 0 : return 1;
2979 : : }
2980 : 10 : }
2981 : :
2982 : 573 : return 0;
2983 : 1 : out:
2984 : 1 : return 1;
2985 : 24 : }
2986 : :
2987 : : int
2988 : 424 : main(int argc, char **argv)
2989 : : {
2990 : 424 : struct spdk_app_opts opts = {};
2991 : : int rc;
2992 : :
2993 : : /* Use the runtime PID to set the random seed */
2994 : 424 : srand(getpid());
2995 : :
2996 : 424 : spdk_app_opts_init(&opts, sizeof(opts));
2997 : 424 : opts.name = "bdevperf";
2998 : 424 : opts.rpc_addr = NULL;
2999 : 424 : opts.shutdown_cb = spdk_bdevperf_shutdown_cb;
3000 : :
3001 [ - + - + ]: 443 : if ((rc = spdk_app_parse_args(argc, argv, &opts, "Zzfq:o:t:w:k:CEF:J:M:P:S:T:Xlj:DU", NULL,
3002 : 19 : bdevperf_parse_arg, bdevperf_usage)) !=
3003 : : SPDK_APP_PARSE_ARGS_SUCCESS) {
3004 : 0 : return rc;
3005 : : }
3006 : :
3007 : : /* Set the default address if no rpc_addr was provided in args
3008 : : * and RPC is used for starting tests */
3009 [ + + + + : 424 : if (g_wait_for_tests && opts.rpc_addr == NULL) {
+ + ]
3010 : 82 : opts.rpc_addr = SPDK_DEFAULT_RPC_ADDR;
3011 : 5 : }
3012 : :
3013 [ - + ]: 424 : if (read_job_config()) {
3014 : 0 : bdevperf_fini();
3015 : 0 : return 1;
3016 : : }
3017 : :
3018 [ - + ]: 424 : if (g_rpc_log_file != NULL) {
3019 : 0 : opts.rpc_log_file = g_rpc_log_file;
3020 : 0 : }
3021 : :
3022 [ + + - + : 424 : if (verify_test_params() != 0 && !g_wait_for_tests) {
- + ]
3023 : 0 : spdk_app_usage();
3024 : 0 : bdevperf_usage();
3025 : 0 : bdevperf_fini();
3026 [ # # ]: 0 : exit(1);
3027 : : }
3028 : :
3029 : 424 : rc = spdk_app_start(&opts, bdevperf_run, NULL);
3030 : :
3031 : 424 : spdk_app_fini();
3032 : 424 : bdevperf_fini();
3033 : 424 : return rc;
3034 : 19 : }
|