Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2017 Intel Corporation.
3 : : * All rights reserved.
4 : : * Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
5 : : */
6 : :
7 : : #include "spdk/stdinc.h"
8 : :
9 : : #include "spdk/bdev.h"
10 : : #include "spdk/bdev_zone.h"
11 : : #include "spdk/accel.h"
12 : : #include "spdk/env.h"
13 : : #include "spdk/init.h"
14 : : #include "spdk/thread.h"
15 : : #include "spdk/log.h"
16 : : #include "spdk/string.h"
17 : : #include "spdk/queue.h"
18 : : #include "spdk/util.h"
19 : : #include "spdk/rpc.h"
20 : :
21 : : #include "spdk_internal/event.h"
22 : :
23 : : #include "config-host.h"
24 : : #include "fio.h"
25 : : #include "optgroup.h"
26 : :
27 : : #ifdef for_each_rw_ddir
28 : : #define FIO_HAS_ZBD (FIO_IOOPS_VERSION >= 26)
29 : : #else
30 : : #define FIO_HAS_ZBD (0)
31 : : #endif
32 : :
33 : : /* FreeBSD is missing CLOCK_MONOTONIC_RAW,
34 : : * so alternative is provided. */
35 : : #ifndef CLOCK_MONOTONIC_RAW /* Defined in glibc bits/time.h */
36 : : #define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
37 : : #endif
38 : :
39 : : struct spdk_fio_options {
40 : : void *pad;
41 : : char *conf;
42 : : char *json_conf;
43 : : char *env_context;
44 : : char *log_flags;
45 : : unsigned mem_mb;
46 : : int mem_single_seg;
47 : : int initial_zone_reset;
48 : : int zone_append;
49 : : char *rpc_listen_addr;
50 : : };
51 : :
52 : : struct spdk_fio_request {
53 : : struct io_u *io;
54 : : struct thread_data *td;
55 : : };
56 : :
57 : : struct spdk_fio_target {
58 : : struct spdk_bdev *bdev;
59 : : struct spdk_bdev_desc *desc;
60 : : struct spdk_io_channel *ch;
61 : : bool zone_append_enabled;
62 : :
63 : : TAILQ_ENTRY(spdk_fio_target) link;
64 : : };
65 : :
66 : : struct spdk_fio_thread {
67 : : struct thread_data *td; /* fio thread context */
68 : : struct spdk_thread *thread; /* spdk thread context */
69 : :
70 : : TAILQ_HEAD(, spdk_fio_target) targets;
71 : : bool failed; /* true if the thread failed to initialize */
72 : :
73 : : struct io_u **iocq; /* io completion queue */
74 : : unsigned int iocq_count; /* number of iocq entries filled by last getevents */
75 : : unsigned int iocq_size; /* number of iocq entries allocated */
76 : :
77 : : TAILQ_ENTRY(spdk_fio_thread) link;
78 : : };
79 : :
80 : : struct spdk_fio_zone_cb_arg {
81 : : struct spdk_fio_target *target;
82 : : struct spdk_bdev_zone_info *spdk_zones;
83 : : int completed;
84 : : uint64_t offset_blocks;
85 : : struct zbd_zone *fio_zones;
86 : : unsigned int nr_zones;
87 : : };
88 : :
89 : : /* On App Thread (oat) context used for making sync calls from async calls. */
90 : : struct spdk_fio_oat_ctx {
91 : : union {
92 : : struct spdk_fio_setup_args {
93 : : struct thread_data *td;
94 : : } sa;
95 : : struct spdk_fio_bdev_get_zoned_model_args {
96 : : struct fio_file *f;
97 : : enum zbd_zoned_model *model;
98 : : } zma;
99 : : struct spdk_fio_bdev_get_max_open_zones_args {
100 : : struct fio_file *f;
101 : : unsigned int *max_open_zones;
102 : : } moza;
103 : : } u;
104 : : pthread_mutex_t mutex;
105 : : pthread_cond_t cond;
106 : : int ret;
107 : : };
108 : :
109 : : static bool g_spdk_env_initialized = false;
110 : : static const char *g_json_config_file = NULL;
111 : : static const char *g_rpc_listen_addr = SPDK_DEFAULT_RPC_ADDR;
112 : :
113 : : static int spdk_fio_init(struct thread_data *td);
114 : : static void spdk_fio_cleanup(struct thread_data *td);
115 : : static size_t spdk_fio_poll_thread(struct spdk_fio_thread *fio_thread);
116 : : static int spdk_fio_handle_options(struct thread_data *td, struct fio_file *f,
117 : : struct spdk_bdev *bdev);
118 : : static int spdk_fio_handle_options_per_target(struct thread_data *td, struct fio_file *f);
119 : : static void spdk_fio_setup_oat(void *ctx);
120 : :
121 : : static pthread_t g_init_thread_id = 0;
122 : : static pthread_mutex_t g_init_mtx = PTHREAD_MUTEX_INITIALIZER;
123 : : static pthread_cond_t g_init_cond;
124 : : static bool g_poll_loop = true;
125 : : static TAILQ_HEAD(, spdk_fio_thread) g_threads = TAILQ_HEAD_INITIALIZER(g_threads);
126 : :
127 : : /* Default polling timeout (ns) */
128 : : #define SPDK_FIO_POLLING_TIMEOUT 1000000000ULL
129 : :
130 : : static __thread bool g_internal_thread = false;
131 : :
132 : : /* Run msg_fn on app thread ("oat") and wait for it to call spdk_fio_wake_oat_waiter() */
133 : : static void
134 : 312 : spdk_fio_sync_run_oat(void (*msg_fn)(void *), struct spdk_fio_oat_ctx *ctx)
135 : : {
136 [ - + ]: 312 : assert(!spdk_thread_is_app_thread(NULL));
137 : :
138 [ - + ]: 312 : pthread_mutex_init(&ctx->mutex, NULL);
139 [ - + ]: 312 : pthread_cond_init(&ctx->cond, NULL);
140 [ - + ]: 312 : pthread_mutex_lock(&ctx->mutex);
141 : :
142 : 312 : spdk_thread_send_msg(spdk_thread_get_app_thread(), msg_fn, ctx);
143 : :
144 : : /* Wake up the poll loop in spdk_init_thread_poll() */
145 [ - + ]: 312 : pthread_mutex_lock(&g_init_mtx);
146 [ - + ]: 312 : pthread_cond_signal(&g_init_cond);
147 [ - + ]: 312 : pthread_mutex_unlock(&g_init_mtx);
148 : :
149 : : /* Wait for msg_fn() to call spdk_fio_wake_oat_waiter() */
150 [ - + - + ]: 312 : pthread_cond_wait(&ctx->cond, &ctx->mutex);
151 [ - + ]: 312 : pthread_mutex_unlock(&ctx->mutex);
152 : :
153 [ - + ]: 312 : pthread_mutex_destroy(&ctx->mutex);
154 [ - + ]: 312 : pthread_cond_destroy(&ctx->cond);
155 : 312 : }
156 : :
157 : : static void
158 : 312 : spdk_fio_wake_oat_waiter(struct spdk_fio_oat_ctx *ctx)
159 : : {
160 [ - + ]: 312 : pthread_mutex_lock(&ctx->mutex);
161 [ - + ]: 312 : pthread_cond_signal(&ctx->cond);
162 [ - + ]: 312 : pthread_mutex_unlock(&ctx->mutex);
163 : 312 : }
164 : :
165 : : static int
166 : 357 : spdk_fio_schedule_thread(struct spdk_thread *thread)
167 : : {
168 : : struct spdk_fio_thread *fio_thread;
169 : :
170 [ + + + - ]: 357 : if (g_internal_thread) {
171 : : /* Do nothing. */
172 : 357 : return 0;
173 : : }
174 : :
175 : 0 : fio_thread = spdk_thread_get_ctx(thread);
176 : :
177 [ # # ]: 0 : pthread_mutex_lock(&g_init_mtx);
178 : 0 : TAILQ_INSERT_TAIL(&g_threads, fio_thread, link);
179 [ # # ]: 0 : pthread_mutex_unlock(&g_init_mtx);
180 : :
181 : 0 : return 0;
182 : : }
183 : :
184 : : static int
185 : 357 : spdk_fio_init_thread(struct thread_data *td)
186 : : {
187 : : struct spdk_fio_thread *fio_thread;
188 : : struct spdk_thread *thread;
189 : :
190 : 357 : g_internal_thread = true;
191 : 357 : thread = spdk_thread_create("fio_thread", NULL);
192 : 357 : g_internal_thread = false;
193 [ - + ]: 357 : if (!thread) {
194 : 0 : SPDK_ERRLOG("failed to allocate thread\n");
195 : 0 : return -1;
196 : : }
197 : :
198 : 357 : fio_thread = spdk_thread_get_ctx(thread);
199 : 357 : fio_thread->td = td;
200 : 357 : fio_thread->thread = thread;
201 : 357 : td->io_ops_data = fio_thread;
202 : :
203 : 357 : spdk_set_thread(thread);
204 : :
205 : 357 : fio_thread->iocq_size = td->o.iodepth;
206 : 357 : fio_thread->iocq = calloc(fio_thread->iocq_size, sizeof(struct io_u *));
207 [ - + ]: 357 : assert(fio_thread->iocq != NULL);
208 : :
209 : 357 : TAILQ_INIT(&fio_thread->targets);
210 : :
211 : 357 : return 0;
212 : : }
213 : :
214 : : static void
215 : 357 : spdk_fio_bdev_close_targets(void *arg)
216 : : {
217 : 357 : struct spdk_fio_thread *fio_thread = arg;
218 : : struct spdk_fio_target *target, *tmp;
219 : :
220 [ + + ]: 696 : TAILQ_FOREACH_SAFE(target, &fio_thread->targets, link, tmp) {
221 [ + + ]: 339 : TAILQ_REMOVE(&fio_thread->targets, target, link);
222 : 339 : spdk_put_io_channel(target->ch);
223 : 339 : spdk_bdev_close(target->desc);
224 : 339 : free(target);
225 : : }
226 : 357 : }
227 : :
228 : : static void
229 : 357 : spdk_fio_cleanup_thread(struct spdk_fio_thread *fio_thread)
230 : : {
231 : 357 : spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_close_targets, fio_thread);
232 : :
233 [ - + ]: 357 : pthread_mutex_lock(&g_init_mtx);
234 : 357 : TAILQ_INSERT_TAIL(&g_threads, fio_thread, link);
235 [ - + ]: 357 : pthread_mutex_unlock(&g_init_mtx);
236 : 357 : }
237 : :
238 : : static void
239 : 1998244 : spdk_fio_calc_timeout(struct spdk_fio_thread *fio_thread, struct timespec *ts)
240 : : {
241 : : uint64_t timeout, now;
242 : :
243 [ + + ]: 1998244 : if (spdk_thread_has_active_pollers(fio_thread->thread)) {
244 : 1948979 : return;
245 : : }
246 : :
247 : 49265 : timeout = spdk_thread_next_poller_expiration(fio_thread->thread);
248 : 49265 : now = spdk_get_ticks();
249 : :
250 [ + + ]: 49265 : if (timeout == 0) {
251 : 11 : timeout = now + (SPDK_FIO_POLLING_TIMEOUT * spdk_get_ticks_hz()) / SPDK_SEC_TO_NSEC;
252 : : }
253 : :
254 [ + + ]: 49265 : if (timeout > now) {
255 [ - + ]: 49242 : timeout = ((timeout - now) * SPDK_SEC_TO_NSEC) / spdk_get_ticks_hz() +
256 : 49242 : ts->tv_sec * SPDK_SEC_TO_NSEC + ts->tv_nsec;
257 : :
258 : 49242 : ts->tv_sec = timeout / SPDK_SEC_TO_NSEC;
259 : 49242 : ts->tv_nsec = timeout % SPDK_SEC_TO_NSEC;
260 : : }
261 : : }
262 : :
263 : : static void
264 : 47 : spdk_fio_bdev_init_done(int rc, void *cb_arg)
265 : : {
266 : 47 : *(bool *)cb_arg = true;
267 : :
268 [ + - ]: 47 : if (spdk_rpc_initialize(g_rpc_listen_addr, NULL) == 0) {
269 : 47 : spdk_rpc_set_state(SPDK_RPC_RUNTIME);
270 : : }
271 : 47 : }
272 : :
273 : : static void
274 : 47 : spdk_fio_bdev_init_start(void *arg)
275 : : {
276 : 47 : bool *done = arg;
277 : :
278 : 47 : spdk_subsystem_init_from_json_config(g_json_config_file, SPDK_DEFAULT_RPC_ADDR,
279 : : spdk_fio_bdev_init_done, done, true);
280 : 47 : }
281 : :
282 : : static void
283 : 47 : spdk_fio_bdev_fini_done(void *cb_arg)
284 : : {
285 : 47 : *(bool *)cb_arg = true;
286 : :
287 : 47 : spdk_rpc_finish();
288 : 47 : }
289 : :
290 : : static void
291 : 47 : spdk_fio_bdev_fini_start(void *arg)
292 : : {
293 : 47 : bool *done = arg;
294 : :
295 : 47 : spdk_subsystem_fini(spdk_fio_bdev_fini_done, done);
296 : 47 : }
297 : :
298 : : static void *
299 : 47 : spdk_init_thread_poll(void *arg)
300 : : {
301 : 47 : struct spdk_fio_options *eo = arg;
302 : : struct spdk_fio_thread *fio_thread;
303 : : struct spdk_fio_thread *thread, *tmp;
304 : 13 : struct spdk_env_opts opts;
305 : 13 : bool done;
306 : : int rc;
307 : 13 : struct timespec ts;
308 : 47 : struct thread_data td = {};
309 : :
310 : : /* Create a dummy thread data for use on the initialization thread. */
311 : 47 : td.o.iodepth = 32;
312 : 47 : td.eo = eo;
313 : :
314 : : /* Parse the SPDK configuration file */
315 : 47 : eo = arg;
316 : :
317 [ - + - - ]: 47 : if (eo->conf && eo->json_conf) {
318 : 0 : SPDK_ERRLOG("Cannot provide two types of configuration files\n");
319 : 0 : rc = EINVAL;
320 : 0 : goto err_exit;
321 [ - + - - ]: 47 : } else if (eo->conf && strlen(eo->conf)) {
322 : 0 : g_json_config_file = eo->conf;
323 [ + - + - ]: 47 : } else if (eo->json_conf && strlen(eo->json_conf)) {
324 : 47 : g_json_config_file = eo->json_conf;
325 : : } else {
326 : 0 : SPDK_ERRLOG("No configuration file provided\n");
327 : 0 : rc = EINVAL;
328 : 0 : goto err_exit;
329 : : }
330 : :
331 : : /* Initialize the RPC listen address */
332 [ - + ]: 47 : if (eo->rpc_listen_addr) {
333 : 0 : g_rpc_listen_addr = eo->rpc_listen_addr;
334 : : }
335 : :
336 : : /* Initialize the environment library */
337 : 47 : spdk_env_opts_init(&opts);
338 : 47 : opts.name = "fio";
339 : :
340 [ + + ]: 47 : if (eo->mem_mb) {
341 : 4 : opts.mem_size = eo->mem_mb;
342 : : }
343 : 47 : opts.hugepage_single_segments = eo->mem_single_seg;
344 [ - + ]: 47 : if (eo->env_context) {
345 : 0 : opts.env_context = eo->env_context;
346 : : }
347 : :
348 [ - + ]: 47 : if (spdk_env_init(&opts) < 0) {
349 : 0 : SPDK_ERRLOG("Unable to initialize SPDK env\n");
350 : 0 : rc = EINVAL;
351 : 0 : goto err_exit;
352 : : }
353 : 47 : spdk_unaffinitize_thread();
354 : :
355 [ - + ]: 47 : if (eo->log_flags) {
356 [ # # ]: 0 : char *tok = strtok(eo->log_flags, ",");
357 : : do {
358 : 0 : rc = spdk_log_set_flag(tok);
359 [ # # ]: 0 : if (rc < 0) {
360 : 0 : SPDK_ERRLOG("unknown spdk log flag %s\n", tok);
361 : 0 : rc = EINVAL;
362 : 0 : goto err_exit;
363 : : }
364 [ # # # # ]: 0 : } while ((tok = strtok(NULL, ",")) != NULL);
365 : : #ifdef DEBUG
366 : 0 : spdk_log_set_print_level(SPDK_LOG_DEBUG);
367 : : #endif
368 : : }
369 : :
370 : 47 : spdk_thread_lib_init(spdk_fio_schedule_thread, sizeof(struct spdk_fio_thread));
371 : :
372 : : /* Create an SPDK thread temporarily */
373 : 47 : rc = spdk_fio_init_thread(&td);
374 [ - + ]: 47 : if (rc < 0) {
375 : 0 : SPDK_ERRLOG("Failed to create initialization thread\n");
376 : 0 : goto err_exit;
377 : : }
378 : :
379 : 47 : fio_thread = td.io_ops_data;
380 : :
381 : : /* Initialize the bdev layer */
382 : 47 : done = false;
383 : 47 : spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_init_start, &done);
384 : :
385 : : do {
386 : 13669312 : spdk_fio_poll_thread(fio_thread);
387 [ + + + + ]: 13669312 : } while (!done);
388 : :
389 : : /*
390 : : * Continue polling until there are no more events.
391 : : * This handles any final events posted by pollers.
392 : : */
393 [ - + ]: 47 : while (spdk_fio_poll_thread(fio_thread) > 0) {};
394 : :
395 : : /* Set condition variable */
396 [ - + ]: 47 : pthread_mutex_lock(&g_init_mtx);
397 [ - + ]: 47 : pthread_cond_signal(&g_init_cond);
398 : :
399 [ - + ]: 47 : pthread_mutex_unlock(&g_init_mtx);
400 : :
401 [ + + + + ]: 4343069 : while (g_poll_loop) {
402 : 4343022 : spdk_fio_poll_thread(fio_thread);
403 : :
404 [ - + ]: 4343022 : pthread_mutex_lock(&g_init_mtx);
405 [ + + ]: 4343022 : if (!TAILQ_EMPTY(&g_threads)) {
406 [ + + ]: 15777203 : TAILQ_FOREACH_SAFE(thread, &g_threads, link, tmp) {
407 [ - + ]: 13432425 : if (spdk_thread_is_exited(thread->thread)) {
408 [ # # ]: 0 : TAILQ_REMOVE(&g_threads, thread, link);
409 : 0 : free(thread->iocq);
410 : 0 : spdk_thread_destroy(thread->thread);
411 : : } else {
412 : 13432425 : spdk_fio_poll_thread(thread);
413 : : }
414 : : }
415 : :
416 : : /* If there are exiting threads to poll, don't sleep. */
417 [ - + ]: 2344778 : pthread_mutex_unlock(&g_init_mtx);
418 : 2344778 : continue;
419 : : }
420 : :
421 : : /* Figure out how long to sleep. */
422 [ - + ]: 1998244 : clock_gettime(CLOCK_MONOTONIC, &ts);
423 : 1998244 : spdk_fio_calc_timeout(fio_thread, &ts);
424 : :
425 [ - + - + : 1998244 : rc = pthread_cond_timedwait(&g_init_cond, &g_init_mtx, &ts);
- + ]
426 [ - + ]: 1998244 : pthread_mutex_unlock(&g_init_mtx);
427 : :
428 [ + + - + ]: 1998244 : if (rc != 0 && rc != ETIMEDOUT) {
429 : 0 : break;
430 : : }
431 : : }
432 : :
433 : 47 : spdk_fio_cleanup_thread(fio_thread);
434 : :
435 : : /* Finalize the bdev layer */
436 : 47 : done = false;
437 : 47 : spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_fini_start, &done);
438 : :
439 : : do {
440 : 704593 : spdk_fio_poll_thread(fio_thread);
441 : :
442 [ + + ]: 4750081 : TAILQ_FOREACH_SAFE(thread, &g_threads, link, tmp) {
443 : 4045488 : spdk_fio_poll_thread(thread);
444 : : }
445 [ + + + + ]: 704593 : } while (!done);
446 : :
447 : : /* Now exit all the threads */
448 [ + + ]: 404 : TAILQ_FOREACH(thread, &g_threads, link) {
449 : 357 : spdk_set_thread(thread->thread);
450 : 357 : spdk_thread_exit(thread->thread);
451 : 357 : spdk_set_thread(NULL);
452 : : }
453 : :
454 : : /* And wait for them to gracefully exit */
455 [ + + ]: 141 : while (!TAILQ_EMPTY(&g_threads)) {
456 [ + + ]: 808 : TAILQ_FOREACH_SAFE(thread, &g_threads, link, tmp) {
457 [ + + ]: 714 : if (spdk_thread_is_exited(thread->thread)) {
458 [ + + ]: 357 : TAILQ_REMOVE(&g_threads, thread, link);
459 : 357 : free(thread->iocq);
460 : 357 : spdk_thread_destroy(thread->thread);
461 : : } else {
462 : 357 : spdk_thread_poll(thread->thread, 0, 0);
463 : : }
464 : : }
465 : : }
466 : :
467 : 47 : pthread_exit(NULL);
468 : :
469 : 0 : err_exit:
470 : 0 : exit(rc);
471 : : return NULL;
472 : : }
473 : :
474 : : static int
475 : 47 : spdk_fio_init_env(struct thread_data *td)
476 : : {
477 : 13 : pthread_condattr_t attr;
478 : 47 : int rc = -1;
479 : :
480 [ - + - + ]: 47 : if (pthread_condattr_init(&attr)) {
481 : 0 : SPDK_ERRLOG("Unable to initialize condition variable\n");
482 : 0 : return -1;
483 : : }
484 : :
485 [ - + - + ]: 47 : if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) {
486 : 0 : SPDK_ERRLOG("Unable to initialize condition variable\n");
487 : 0 : goto out;
488 : : }
489 : :
490 [ - + - + ]: 47 : if (pthread_cond_init(&g_init_cond, &attr)) {
491 : 0 : SPDK_ERRLOG("Unable to initialize condition variable\n");
492 : 0 : goto out;
493 : : }
494 : :
495 : : /*
496 : : * Spawn a thread to handle initialization operations and to poll things
497 : : * like the admin queues periodically.
498 : : */
499 [ - + - + ]: 47 : rc = pthread_create(&g_init_thread_id, NULL, &spdk_init_thread_poll, td->eo);
500 [ - + ]: 47 : if (rc != 0) {
501 : 0 : SPDK_ERRLOG("Unable to spawn thread to poll admin queue. It won't be polled.\n");
502 : : }
503 : :
504 : : /* Wait for background thread to advance past the initialization */
505 [ - + ]: 47 : pthread_mutex_lock(&g_init_mtx);
506 [ - + - + ]: 47 : pthread_cond_wait(&g_init_cond, &g_init_mtx);
507 [ - + ]: 47 : pthread_mutex_unlock(&g_init_mtx);
508 : 47 : out:
509 [ - + ]: 47 : pthread_condattr_destroy(&attr);
510 : 47 : return rc;
511 : : }
512 : :
513 : : static bool
514 : 0 : fio_redirected_to_dev_null(void)
515 : : {
516 : 0 : char path[PATH_MAX] = "";
517 : : ssize_t ret;
518 : :
519 : 0 : ret = readlink("/proc/self/fd/1", path, sizeof(path));
520 : :
521 [ # # # # ]: 0 : if (ret == -1 || strcmp(path, "/dev/null") != 0) {
522 : 0 : return false;
523 : : }
524 : :
525 : 0 : ret = readlink("/proc/self/fd/2", path, sizeof(path));
526 : :
527 [ # # # # ]: 0 : if (ret == -1 || strcmp(path, "/dev/null") != 0) {
528 : 0 : return false;
529 : : }
530 : :
531 : 0 : return true;
532 : : }
533 : :
534 : : static int
535 : 622 : spdk_fio_init_spdk_env(struct thread_data *td)
536 : : {
537 : : static pthread_mutex_t setup_lock = PTHREAD_MUTEX_INITIALIZER;
538 : :
539 [ - + ]: 622 : pthread_mutex_lock(&setup_lock);
540 [ + + + + ]: 622 : if (!g_spdk_env_initialized) {
541 [ - + ]: 47 : if (spdk_fio_init_env(td)) {
542 [ # # ]: 0 : pthread_mutex_unlock(&setup_lock);
543 : 0 : SPDK_ERRLOG("failed to initialize\n");
544 : 0 : return -1;
545 : : }
546 : :
547 : 47 : g_spdk_env_initialized = true;
548 : : }
549 [ - + ]: 622 : pthread_mutex_unlock(&setup_lock);
550 : :
551 : 622 : return 0;
552 : : }
553 : :
554 : : /* Called for each thread to fill in the 'real_file_size' member for
555 : : * each file associated with this thread. This is called prior to
556 : : * the init operation (spdk_fio_init()) below. This call will occur
557 : : * on the initial start up thread if 'create_serialize' is true, or
558 : : * on the thread actually associated with 'thread_data' if 'create_serialize'
559 : : * is false.
560 : : */
561 : : static int
562 : 310 : spdk_fio_setup(struct thread_data *td)
563 : : {
564 : 310 : struct spdk_fio_oat_ctx ctx = { 0 };
565 : :
566 : : /*
567 : : * If we're running in a daemonized FIO instance, it's possible
568 : : * fd 1/2 were re-used for something important by FIO. Newer fio
569 : : * versions are careful to redirect those to /dev/null, but if we're
570 : : * not, we'll abort early, so we don't accidentally write messages to
571 : : * an important file, etc.
572 : : */
573 [ - + - + : 310 : if (is_backend && !fio_redirected_to_dev_null()) {
- - ]
574 : 0 : char buf[1024];
575 [ # # ]: 0 : snprintf(buf, sizeof(buf),
576 : : "SPDK FIO plugin is in daemon mode, but stdout/stderr "
577 : : "aren't redirected to /dev/null. Aborting.");
578 : 0 : fio_server_text_output(FIO_LOG_ERR, buf, sizeof(buf));
579 : 0 : return -1;
580 : : }
581 : :
582 [ - + ]: 310 : if (!td->o.use_thread) {
583 : 0 : SPDK_ERRLOG("must set thread=1 when using spdk plugin\n");
584 : 0 : return -1;
585 : : }
586 : :
587 [ - + ]: 310 : if (spdk_fio_init_spdk_env(td) != 0) {
588 : 0 : return -1;
589 : : }
590 : :
591 : 310 : ctx.u.sa.td = td;
592 : 310 : spdk_fio_sync_run_oat(spdk_fio_setup_oat, &ctx);
593 : 310 : return ctx.ret;
594 : : }
595 : :
596 : : static int
597 : 8 : _spdk_fio_add_file(void *ctx, struct spdk_bdev *bdev)
598 : : {
599 : 8 : struct thread_data *td = ctx;
600 : :
601 : 8 : add_file(td, spdk_bdev_get_name(bdev), 0, 1);
602 : 8 : return 0;
603 : : }
604 : :
605 : : static void
606 : 310 : spdk_fio_setup_oat(void *_ctx)
607 : : {
608 : 310 : struct spdk_fio_oat_ctx *ctx = _ctx;
609 : 310 : struct thread_data *td = ctx->u.sa.td;
610 : : unsigned int i;
611 : : struct fio_file *f;
612 : :
613 [ + + - + : 310 : if (td->o.nr_files == 1 && strcmp(td->files[0]->file_name, "*") == 0) {
+ + ]
614 : : /* add all available bdevs as fio targets */
615 : 1 : spdk_for_each_bdev_leaf(td, _spdk_fio_add_file);
616 : : }
617 : :
618 [ + - + + : 650 : for_each_file(td, f, i) {
+ - ]
619 : : struct spdk_bdev *bdev;
620 : :
621 [ - + + + ]: 340 : if (strcmp(f->file_name, "*") == 0) {
622 : : /* Explicitly set file size to 0 here to make sure fio doesn't try to
623 : : * actually send I/O to this "*" file.
624 : : */
625 : 1 : f->real_file_size = 0;
626 : 1 : continue;
627 : : }
628 : :
629 : 339 : bdev = spdk_bdev_get_by_name(f->file_name);
630 [ - + ]: 339 : if (!bdev) {
631 : 0 : SPDK_ERRLOG("Unable to find bdev with name %s\n", f->file_name);
632 : 0 : ctx->ret = -1;
633 : 0 : goto out;
634 : : }
635 : :
636 : 617 : f->real_file_size = spdk_bdev_get_num_blocks(bdev) *
637 : 339 : spdk_bdev_get_block_size(bdev);
638 : 339 : f->filetype = FIO_TYPE_BLOCK;
639 : 339 : fio_file_set_size_known(f);
640 : :
641 : 339 : ctx->ret = spdk_fio_handle_options(td, f, bdev);
642 [ - + ]: 339 : if (ctx->ret) {
643 : 0 : goto out;
644 : : }
645 : : }
646 : :
647 : 310 : ctx->ret = 0;
648 : 310 : out:
649 : 310 : spdk_fio_wake_oat_waiter(ctx);
650 : 310 : }
651 : :
652 : : static void
653 : 0 : fio_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
654 : : void *event_ctx)
655 : : {
656 : 0 : SPDK_WARNLOG("Unsupported bdev event: type %d\n", type);
657 : 0 : }
658 : :
659 : : static void
660 : 310 : spdk_fio_bdev_open(void *arg)
661 : : {
662 : 310 : struct thread_data *td = arg;
663 : : struct spdk_fio_thread *fio_thread;
664 : : unsigned int i;
665 : : struct fio_file *f;
666 : : int rc;
667 : :
668 : 310 : fio_thread = td->io_ops_data;
669 : :
670 [ + - + + : 650 : for_each_file(td, f, i) {
+ - ]
671 : : struct spdk_fio_target *target;
672 : :
673 [ - + + + ]: 340 : if (strcmp(f->file_name, "*") == 0) {
674 : 1 : continue;
675 : : }
676 : :
677 : 339 : target = calloc(1, sizeof(*target));
678 [ - + ]: 339 : if (!target) {
679 : 0 : SPDK_ERRLOG("Unable to allocate memory for I/O target.\n");
680 : 0 : fio_thread->failed = true;
681 : 0 : return;
682 : : }
683 : :
684 : 339 : rc = spdk_bdev_open_ext(f->file_name, true, fio_bdev_event_cb, NULL,
685 : : &target->desc);
686 [ - + ]: 339 : if (rc) {
687 : 0 : SPDK_ERRLOG("Unable to open bdev %s\n", f->file_name);
688 : 0 : free(target);
689 : 0 : fio_thread->failed = true;
690 : 0 : return;
691 : : }
692 : :
693 : 339 : target->bdev = spdk_bdev_desc_get_bdev(target->desc);
694 : :
695 : 339 : target->ch = spdk_bdev_get_io_channel(target->desc);
696 [ - + ]: 339 : if (!target->ch) {
697 : 0 : SPDK_ERRLOG("Unable to get I/O channel for bdev.\n");
698 : 0 : spdk_bdev_close(target->desc);
699 : 0 : free(target);
700 : 0 : fio_thread->failed = true;
701 : 0 : return;
702 : : }
703 : :
704 : 339 : f->engine_data = target;
705 : :
706 : 339 : rc = spdk_fio_handle_options_per_target(td, f);
707 [ - + ]: 339 : if (rc) {
708 : 0 : SPDK_ERRLOG("Failed to handle options for: %s\n", f->file_name);
709 : 0 : f->engine_data = NULL;
710 : 0 : spdk_put_io_channel(target->ch);
711 : 0 : spdk_bdev_close(target->desc);
712 : 0 : free(target);
713 : 0 : fio_thread->failed = true;
714 : 0 : return;
715 : : }
716 : :
717 : 339 : TAILQ_INSERT_TAIL(&fio_thread->targets, target, link);
718 : : }
719 : : }
720 : :
721 : : /* Called for each thread, on that thread, shortly after the thread
722 : : * starts.
723 : : *
724 : : * Also called by spdk_fio_report_zones(), since we need an I/O channel
725 : : * in order to get the zone report. (fio calls the .report_zones callback
726 : : * before it calls the .init callback.)
727 : : * Therefore, if fio was run with --zonemode=zbd, the thread will already
728 : : * be initialized by the time that fio calls the .init callback.
729 : : */
730 : : static int
731 : 312 : spdk_fio_init(struct thread_data *td)
732 : : {
733 : : struct spdk_fio_thread *fio_thread;
734 : : int rc;
735 : :
736 [ - + ]: 312 : if (spdk_fio_init_spdk_env(td) != 0) {
737 : 0 : return -1;
738 : : }
739 : :
740 : : /* If thread has already been initialized, do nothing. */
741 [ + + ]: 312 : if (td->io_ops_data) {
742 : 2 : return 0;
743 : : }
744 : :
745 : 310 : rc = spdk_fio_init_thread(td);
746 [ - + ]: 310 : if (rc) {
747 : 0 : return rc;
748 : : }
749 : :
750 : 310 : fio_thread = td->io_ops_data;
751 [ - + ]: 310 : assert(fio_thread);
752 : 310 : fio_thread->failed = false;
753 : :
754 : 310 : spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_open, td);
755 : :
756 [ + + ]: 630 : while (spdk_fio_poll_thread(fio_thread) > 0) {}
757 : :
758 [ - + - + ]: 310 : if (fio_thread->failed) {
759 : 0 : return -1;
760 : : }
761 : :
762 : 310 : return 0;
763 : : }
764 : :
765 : : static void
766 : 310 : spdk_fio_cleanup(struct thread_data *td)
767 : : {
768 : 310 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
769 : :
770 : 310 : spdk_fio_cleanup_thread(fio_thread);
771 : 310 : td->io_ops_data = NULL;
772 : 310 : }
773 : :
774 : : static int
775 : 5732 : spdk_fio_open(struct thread_data *td, struct fio_file *f)
776 : : {
777 : :
778 : 5732 : return 0;
779 : : }
780 : :
781 : : static int
782 : 5730 : spdk_fio_close(struct thread_data *td, struct fio_file *f)
783 : : {
784 : 5730 : return 0;
785 : : }
786 : :
787 : : static int
788 : 308 : spdk_fio_iomem_alloc(struct thread_data *td, size_t total_mem)
789 : : {
790 : 308 : td->orig_buffer = spdk_dma_zmalloc(total_mem, 0x1000, NULL);
791 : 308 : return td->orig_buffer == NULL;
792 : : }
793 : :
794 : : static void
795 : 310 : spdk_fio_iomem_free(struct thread_data *td)
796 : : {
797 : 310 : spdk_dma_free(td->orig_buffer);
798 : 310 : }
799 : :
800 : : static int
801 : 4947 : spdk_fio_io_u_init(struct thread_data *td, struct io_u *io_u)
802 : : {
803 : : struct spdk_fio_request *fio_req;
804 : :
805 : 4947 : io_u->engine_data = NULL;
806 : :
807 : 4947 : fio_req = calloc(1, sizeof(*fio_req));
808 [ - + ]: 4947 : if (fio_req == NULL) {
809 : 0 : return 1;
810 : : }
811 : 4947 : fio_req->io = io_u;
812 : 4947 : fio_req->td = td;
813 : :
814 : 4947 : io_u->engine_data = fio_req;
815 : :
816 : 4947 : return 0;
817 : : }
818 : :
819 : : static void
820 : 4945 : spdk_fio_io_u_free(struct thread_data *td, struct io_u *io_u)
821 : : {
822 : 4945 : struct spdk_fio_request *fio_req = io_u->engine_data;
823 : :
824 [ + - ]: 4945 : if (fio_req) {
825 [ - + ]: 4945 : assert(fio_req->io == io_u);
826 : 4945 : free(fio_req);
827 : 4945 : io_u->engine_data = NULL;
828 : : }
829 : 4945 : }
830 : :
831 : : static void
832 : 39363533 : spdk_fio_completion_cb(struct spdk_bdev_io *bdev_io,
833 : : bool success,
834 : : void *cb_arg)
835 : : {
836 : 39363533 : struct spdk_fio_request *fio_req = cb_arg;
837 : 39363533 : struct thread_data *td = fio_req->td;
838 : 39363533 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
839 : :
840 [ - + ]: 39363533 : assert(fio_thread->iocq_count < fio_thread->iocq_size);
841 [ + - ]: 39363533 : fio_req->io->error = success ? 0 : EIO;
842 : 39363533 : fio_thread->iocq[fio_thread->iocq_count++] = fio_req->io;
843 : :
844 : 39363533 : spdk_bdev_free_io(bdev_io);
845 : 39363533 : }
846 : :
847 : : #if FIO_IOOPS_VERSION >= 24
848 : : typedef enum fio_q_status fio_q_status_t;
849 : : #else
850 : : typedef int fio_q_status_t;
851 : : #endif
852 : :
853 : : static uint64_t
854 : 268254 : spdk_fio_zone_bytes_to_blocks(struct spdk_bdev *bdev, uint64_t offset_bytes, uint64_t *zone_start,
855 : : uint64_t num_bytes, uint64_t *num_blocks)
856 : : {
857 : 268254 : uint32_t block_size = spdk_bdev_get_block_size(bdev);
858 [ # # ]: 268254 : *zone_start = spdk_bdev_get_zone_id(bdev, offset_bytes / block_size);
859 [ # # ]: 268254 : *num_blocks = num_bytes / block_size;
860 [ # # # # ]: 268254 : return (offset_bytes % block_size) | (num_bytes % block_size);
861 : : }
862 : :
863 : : static fio_q_status_t
864 : 39363533 : spdk_fio_queue(struct thread_data *td, struct io_u *io_u)
865 : : {
866 : 39363533 : int rc = 1;
867 : 39363533 : struct spdk_fio_request *fio_req = io_u->engine_data;
868 : 39363533 : struct spdk_fio_target *target = io_u->file->engine_data;
869 : :
870 [ - + ]: 39363533 : assert(fio_req->td == td);
871 : :
872 [ - + ]: 39363533 : if (!target) {
873 : 0 : SPDK_ERRLOG("Unable to look up correct I/O target.\n");
874 : 0 : fio_req->io->error = ENODEV;
875 : 0 : return FIO_Q_COMPLETED;
876 : : }
877 : :
878 [ + + + - : 39363533 : switch (io_u->ddir) {
- ]
879 : 7845500 : case DDIR_READ:
880 : 7845500 : rc = spdk_bdev_read(target->desc, target->ch,
881 : 7845500 : io_u->buf, io_u->offset, io_u->xfer_buflen,
882 : : spdk_fio_completion_cb, fio_req);
883 : 7845500 : break;
884 : 20660780 : case DDIR_WRITE:
885 [ + + + - ]: 20660780 : if (!target->zone_append_enabled) {
886 : 20392526 : rc = spdk_bdev_write(target->desc, target->ch,
887 : 20392526 : io_u->buf, io_u->offset, io_u->xfer_buflen,
888 : : spdk_fio_completion_cb, fio_req);
889 : : } else {
890 : 0 : uint64_t zone_start, num_blocks;
891 [ - + ]: 268254 : if (spdk_fio_zone_bytes_to_blocks(target->bdev, io_u->offset, &zone_start,
892 : 268254 : io_u->xfer_buflen, &num_blocks) != 0) {
893 : 0 : rc = -EINVAL;
894 : 0 : break;
895 : : }
896 : 268254 : rc = spdk_bdev_zone_append(target->desc, target->ch, io_u->buf,
897 : : zone_start, num_blocks, spdk_fio_completion_cb,
898 : : fio_req);
899 : : }
900 : 20660780 : break;
901 : 10857253 : case DDIR_TRIM:
902 : 10857253 : rc = spdk_bdev_unmap(target->desc, target->ch,
903 : 10857253 : io_u->offset, io_u->xfer_buflen,
904 : : spdk_fio_completion_cb, fio_req);
905 : 10857253 : break;
906 : 0 : case DDIR_SYNC:
907 : 0 : rc = spdk_bdev_flush(target->desc, target->ch,
908 : 0 : io_u->offset, io_u->xfer_buflen,
909 : : spdk_fio_completion_cb, fio_req);
910 : 0 : break;
911 : 0 : default:
912 : 0 : assert(false);
913 : : break;
914 : : }
915 : :
916 [ - + ]: 39363533 : if (rc == -ENOMEM) {
917 : 0 : return FIO_Q_BUSY;
918 : : }
919 : :
920 [ - + ]: 39363533 : if (rc != 0) {
921 : 0 : fio_req->io->error = abs(rc);
922 : 0 : return FIO_Q_COMPLETED;
923 : : }
924 : :
925 : 39363533 : return FIO_Q_QUEUED;
926 : : }
927 : :
928 : : static struct io_u *
929 : 39363533 : spdk_fio_event(struct thread_data *td, int event)
930 : : {
931 : 39363533 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
932 : :
933 [ - + ]: 39363533 : assert(event >= 0);
934 [ - + ]: 39363533 : assert((unsigned)event < fio_thread->iocq_count);
935 : 39363533 : return fio_thread->iocq[event];
936 : : }
937 : :
938 : : static size_t
939 : 181077749 : spdk_fio_poll_thread(struct spdk_fio_thread *fio_thread)
940 : : {
941 : 181077749 : return spdk_thread_poll(fio_thread->thread, 0, 0);
942 : : }
943 : :
944 : : static int
945 : 5558435 : spdk_fio_getevents(struct thread_data *td, unsigned int min,
946 : : unsigned int max, const struct timespec *t)
947 : : {
948 : 5558435 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
949 : 3461948 : struct timespec t0, t1;
950 : 5558435 : uint64_t timeout = 0;
951 : :
952 [ - + ]: 5558435 : if (t) {
953 : 0 : timeout = t->tv_sec * SPDK_SEC_TO_NSEC + t->tv_nsec;
954 [ # # ]: 0 : clock_gettime(CLOCK_MONOTONIC_RAW, &t0);
955 : : }
956 : :
957 : 5558435 : fio_thread->iocq_count = 0;
958 : :
959 : : for (;;) {
960 : 144221897 : spdk_fio_poll_thread(fio_thread);
961 : :
962 [ + + ]: 144221897 : if (fio_thread->iocq_count >= min) {
963 : 5558435 : return fio_thread->iocq_count;
964 : : }
965 : :
966 [ - + ]: 138663462 : if (t) {
967 [ # # ]: 0 : clock_gettime(CLOCK_MONOTONIC_RAW, &t1);
968 : 0 : uint64_t elapse = ((t1.tv_sec - t0.tv_sec) * SPDK_SEC_TO_NSEC)
969 : 0 : + t1.tv_nsec - t0.tv_nsec;
970 [ # # ]: 0 : if (elapse > timeout) {
971 : 0 : break;
972 : : }
973 : : }
974 : : }
975 : :
976 : 0 : return fio_thread->iocq_count;
977 : : }
978 : :
979 : : static int
980 : 0 : spdk_fio_invalidate(struct thread_data *td, struct fio_file *f)
981 : : {
982 : : /* TODO: This should probably send a flush to the device, but for now just return successful. */
983 : 0 : return 0;
984 : : }
985 : :
986 : : #if FIO_HAS_ZBD
987 : : /* Runs on app thread (oat) */
988 : : static void
989 : 1 : spdk_fio_get_zoned_model_oat(void *arg)
990 : : {
991 : 1 : struct spdk_fio_oat_ctx *ctx = arg;
992 : 1 : struct fio_file *f = ctx->u.zma.f;
993 : 1 : enum zbd_zoned_model *model = ctx->u.zma.model;
994 : : struct spdk_bdev *bdev;
995 : :
996 [ - + ]: 1 : if (f->filetype != FIO_TYPE_BLOCK) {
997 : 0 : SPDK_ERRLOG("Unsupported filetype: %d\n", f->filetype);
998 : 0 : ctx->ret = -EINVAL;
999 : 0 : goto out;
1000 : : }
1001 : :
1002 : 1 : bdev = spdk_bdev_get_by_name(f->file_name);
1003 [ - + ]: 1 : if (!bdev) {
1004 : 0 : SPDK_ERRLOG("Cannot get zoned model, no bdev with name: %s\n", f->file_name);
1005 : 0 : ctx->ret = -ENODEV;
1006 : 0 : goto out;
1007 : : }
1008 : :
1009 [ + - ]: 1 : if (spdk_bdev_is_zoned(bdev)) {
1010 : 1 : *model = ZBD_HOST_MANAGED;
1011 : : } else {
1012 : 0 : *model = ZBD_NONE;
1013 : : }
1014 : :
1015 : 1 : ctx->ret = 0;
1016 : 1 : out:
1017 : 1 : spdk_fio_wake_oat_waiter(ctx);
1018 : 1 : }
1019 : :
1020 : : static int
1021 : 1 : spdk_fio_get_zoned_model(struct thread_data *td, struct fio_file *f, enum zbd_zoned_model *model)
1022 : : {
1023 : 1 : struct spdk_fio_oat_ctx ctx = { 0 };
1024 : :
1025 : 1 : ctx.u.zma.f = f;
1026 : 1 : ctx.u.zma.model = model;
1027 : :
1028 : 1 : spdk_fio_sync_run_oat(spdk_fio_get_zoned_model_oat, &ctx);
1029 : :
1030 : 1 : return ctx.ret;
1031 : : }
1032 : :
1033 : :
1034 : : static void
1035 : 1 : spdk_fio_bdev_get_zone_info_done(struct spdk_bdev_io *bdev_io, bool success, void *arg)
1036 : : {
1037 : 1 : struct spdk_fio_zone_cb_arg *cb_arg = arg;
1038 : : unsigned int i;
1039 : 1 : int handled_zones = 0;
1040 : :
1041 [ - + ]: 1 : if (!success) {
1042 : 0 : spdk_bdev_free_io(bdev_io);
1043 : 0 : cb_arg->completed = -EIO;
1044 : 0 : return;
1045 : : }
1046 : :
1047 [ + + ]: 41 : for (i = 0; i < cb_arg->nr_zones; i++) {
1048 : 40 : struct spdk_bdev_zone_info *zone_src = &cb_arg->spdk_zones[handled_zones];
1049 : 40 : struct zbd_zone *zone_dest = &cb_arg->fio_zones[handled_zones];
1050 : 40 : uint32_t block_size = spdk_bdev_get_block_size(cb_arg->target->bdev);
1051 : :
1052 [ + - - - ]: 40 : switch (zone_src->type) {
1053 : 40 : case SPDK_BDEV_ZONE_TYPE_SEQWR:
1054 : 40 : zone_dest->type = ZBD_ZONE_TYPE_SWR;
1055 : 40 : break;
1056 : 0 : case SPDK_BDEV_ZONE_TYPE_SEQWP:
1057 : 0 : zone_dest->type = ZBD_ZONE_TYPE_SWP;
1058 : 0 : break;
1059 : 0 : case SPDK_BDEV_ZONE_TYPE_CNV:
1060 : 0 : zone_dest->type = ZBD_ZONE_TYPE_CNV;
1061 : 0 : break;
1062 : 0 : default:
1063 : 0 : spdk_bdev_free_io(bdev_io);
1064 : 0 : cb_arg->completed = -EIO;
1065 : 0 : return;
1066 : : }
1067 : :
1068 : 40 : zone_dest->len = spdk_bdev_get_zone_size(cb_arg->target->bdev) * block_size;
1069 : 40 : zone_dest->capacity = zone_src->capacity * block_size;
1070 : 40 : zone_dest->start = zone_src->zone_id * block_size;
1071 : 40 : zone_dest->wp = zone_src->write_pointer * block_size;
1072 : :
1073 [ + - - - : 40 : switch (zone_src->state) {
- - - -
- ]
1074 : 40 : case SPDK_BDEV_ZONE_STATE_EMPTY:
1075 : 40 : zone_dest->cond = ZBD_ZONE_COND_EMPTY;
1076 : 40 : break;
1077 : 0 : case SPDK_BDEV_ZONE_STATE_IMP_OPEN:
1078 : 0 : zone_dest->cond = ZBD_ZONE_COND_IMP_OPEN;
1079 : 0 : break;
1080 : 0 : case SPDK_BDEV_ZONE_STATE_EXP_OPEN:
1081 : 0 : zone_dest->cond = ZBD_ZONE_COND_EXP_OPEN;
1082 : 0 : break;
1083 : 0 : case SPDK_BDEV_ZONE_STATE_FULL:
1084 : 0 : zone_dest->cond = ZBD_ZONE_COND_FULL;
1085 : 0 : break;
1086 : 0 : case SPDK_BDEV_ZONE_STATE_CLOSED:
1087 : 0 : zone_dest->cond = ZBD_ZONE_COND_CLOSED;
1088 : 0 : break;
1089 : 0 : case SPDK_BDEV_ZONE_STATE_READ_ONLY:
1090 : 0 : zone_dest->cond = ZBD_ZONE_COND_READONLY;
1091 : 0 : break;
1092 : 0 : case SPDK_BDEV_ZONE_STATE_OFFLINE:
1093 : 0 : zone_dest->cond = ZBD_ZONE_COND_OFFLINE;
1094 : 0 : break;
1095 : 0 : case SPDK_BDEV_ZONE_STATE_NOT_WP:
1096 : 0 : zone_dest->cond = ZBD_ZONE_COND_NOT_WP;
1097 : : /* Set WP to end of zone for zone types w/o WP (e.g. Conv. zones in SMR) */
1098 : 0 : zone_dest->wp = zone_dest->start + zone_dest->capacity;
1099 : 0 : break;
1100 : 0 : default:
1101 : 0 : spdk_bdev_free_io(bdev_io);
1102 : 0 : cb_arg->completed = -EIO;
1103 : 0 : return;
1104 : : }
1105 : 40 : handled_zones++;
1106 : : }
1107 : :
1108 : 1 : spdk_bdev_free_io(bdev_io);
1109 : 1 : cb_arg->completed = handled_zones;
1110 : : }
1111 : :
1112 : : static void
1113 : 1 : spdk_fio_bdev_get_zone_info(void *arg)
1114 : : {
1115 : 1 : struct spdk_fio_zone_cb_arg *cb_arg = arg;
1116 : 1 : struct spdk_fio_target *target = cb_arg->target;
1117 : : int rc;
1118 : :
1119 : 1 : rc = spdk_bdev_get_zone_info(target->desc, target->ch, cb_arg->offset_blocks,
1120 : 1 : cb_arg->nr_zones, cb_arg->spdk_zones,
1121 : : spdk_fio_bdev_get_zone_info_done, cb_arg);
1122 [ - + ]: 1 : if (rc < 0) {
1123 : 0 : cb_arg->completed = rc;
1124 : : }
1125 : 1 : }
1126 : :
1127 : : static int
1128 : 1 : spdk_fio_report_zones(struct thread_data *td, struct fio_file *f, uint64_t offset,
1129 : : struct zbd_zone *zones, unsigned int nr_zones)
1130 : : {
1131 : : struct spdk_fio_target *target;
1132 : : struct spdk_fio_thread *fio_thread;
1133 : 0 : struct spdk_fio_zone_cb_arg cb_arg;
1134 : : uint32_t block_size;
1135 : : int rc;
1136 : :
1137 [ - + ]: 1 : if (nr_zones == 0) {
1138 : 0 : return 0;
1139 : : }
1140 : :
1141 : : /* spdk_fio_report_zones() is only called before the bdev I/O channels have been created.
1142 : : * Since we need an I/O channel for report_zones(), call spdk_fio_init() to initialize
1143 : : * the thread early.
1144 : : * spdk_fio_report_zones() might be called several times by fio, if e.g. the zone report
1145 : : * for all zones does not fit in the buffer that fio has allocated for the zone report.
1146 : : * It is safe to call spdk_fio_init(), even if the thread has already been initialized.
1147 : : */
1148 : 1 : rc = spdk_fio_init(td);
1149 [ - + ]: 1 : if (rc) {
1150 : 0 : return rc;
1151 : : }
1152 : 1 : fio_thread = td->io_ops_data;
1153 : 1 : target = f->engine_data;
1154 : :
1155 [ - + ]: 1 : assert(fio_thread);
1156 [ - + ]: 1 : assert(target);
1157 : :
1158 : 1 : block_size = spdk_bdev_get_block_size(target->bdev);
1159 : :
1160 : 1 : cb_arg.target = target;
1161 : 1 : cb_arg.completed = 0;
1162 [ # # ]: 1 : cb_arg.offset_blocks = offset / block_size;
1163 : 1 : cb_arg.fio_zones = zones;
1164 [ + - ]: 1 : cb_arg.nr_zones = spdk_min(nr_zones, spdk_bdev_get_num_zones(target->bdev));
1165 : :
1166 : 1 : cb_arg.spdk_zones = calloc(1, sizeof(*cb_arg.spdk_zones) * cb_arg.nr_zones);
1167 [ - + ]: 1 : if (!cb_arg.spdk_zones) {
1168 : 0 : SPDK_ERRLOG("Could not allocate memory for zone report!\n");
1169 : 0 : rc = -ENOMEM;
1170 : 0 : goto cleanup_thread;
1171 : : }
1172 : :
1173 : 1 : spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_get_zone_info, &cb_arg);
1174 : : do {
1175 : 185 : spdk_fio_poll_thread(fio_thread);
1176 [ + + ]: 185 : } while (!cb_arg.completed);
1177 : :
1178 : : /* Free cb_arg.spdk_zones. The report in fio format is stored in cb_arg.fio_zones/zones. */
1179 : 1 : free(cb_arg.spdk_zones);
1180 : :
1181 : 1 : rc = cb_arg.completed;
1182 [ - + ]: 1 : if (rc < 0) {
1183 : 0 : SPDK_ERRLOG("Failed to get zone info: %d\n", rc);
1184 : 0 : goto cleanup_thread;
1185 : : }
1186 : :
1187 : : /* Return the amount of zones successfully copied. */
1188 : 1 : return rc;
1189 : :
1190 : 0 : cleanup_thread:
1191 : 0 : spdk_fio_cleanup(td);
1192 : :
1193 : 0 : return rc;
1194 : : }
1195 : :
1196 : : static void
1197 : 46 : spdk_fio_bdev_zone_reset_done(struct spdk_bdev_io *bdev_io, bool success, void *arg)
1198 : : {
1199 : 46 : struct spdk_fio_zone_cb_arg *cb_arg = arg;
1200 : :
1201 : 46 : spdk_bdev_free_io(bdev_io);
1202 : :
1203 [ - + ]: 46 : if (!success) {
1204 : 0 : cb_arg->completed = -EIO;
1205 : : } else {
1206 : 46 : cb_arg->completed = 1;
1207 : : }
1208 : 46 : }
1209 : :
1210 : : static void
1211 : 46 : spdk_fio_bdev_zone_reset(void *arg)
1212 : : {
1213 : 46 : struct spdk_fio_zone_cb_arg *cb_arg = arg;
1214 : 46 : struct spdk_fio_target *target = cb_arg->target;
1215 : : int rc;
1216 : :
1217 : 46 : rc = spdk_bdev_zone_management(target->desc, target->ch, cb_arg->offset_blocks,
1218 : : SPDK_BDEV_ZONE_RESET,
1219 : : spdk_fio_bdev_zone_reset_done, cb_arg);
1220 [ - + ]: 46 : if (rc < 0) {
1221 : 0 : cb_arg->completed = rc;
1222 : : }
1223 : 46 : }
1224 : :
1225 : : static int
1226 : 7 : spdk_fio_reset_zones(struct spdk_fio_thread *fio_thread, struct spdk_fio_target *target,
1227 : : uint64_t offset, uint64_t length)
1228 : : {
1229 : : uint64_t zone_size_bytes;
1230 : : uint32_t block_size;
1231 : : int rc;
1232 : :
1233 [ - + ]: 7 : assert(fio_thread);
1234 [ - + ]: 7 : assert(target);
1235 : :
1236 : 7 : block_size = spdk_bdev_get_block_size(target->bdev);
1237 : 7 : zone_size_bytes = spdk_bdev_get_zone_size(target->bdev) * block_size;
1238 : :
1239 [ + + ]: 53 : for (uint64_t cur = offset; cur < offset + length; cur += zone_size_bytes) {
1240 [ # # ]: 46 : struct spdk_fio_zone_cb_arg cb_arg = {
1241 : : .target = target,
1242 : : .completed = 0,
1243 : 46 : .offset_blocks = cur / block_size,
1244 : : };
1245 : :
1246 : 46 : spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_zone_reset, &cb_arg);
1247 : : do {
1248 : 660151 : spdk_fio_poll_thread(fio_thread);
1249 [ + + ]: 660151 : } while (!cb_arg.completed);
1250 : :
1251 : 46 : rc = cb_arg.completed;
1252 [ - + ]: 46 : if (rc < 0) {
1253 : 0 : SPDK_ERRLOG("Failed to reset zone: %d\n", rc);
1254 : 0 : return rc;
1255 : : }
1256 : : }
1257 : :
1258 : 7 : return 0;
1259 : : }
1260 : :
1261 : : static int
1262 : 6 : spdk_fio_reset_wp(struct thread_data *td, struct fio_file *f, uint64_t offset, uint64_t length)
1263 : : {
1264 : 6 : return spdk_fio_reset_zones(td->io_ops_data, f->engine_data, offset, length);
1265 : : }
1266 : : #endif
1267 : :
1268 : : #if FIO_IOOPS_VERSION >= 30
1269 : : static void
1270 : 1 : spdk_fio_get_max_open_zones_oat(void *_ctx)
1271 : : {
1272 : 1 : struct spdk_fio_oat_ctx *ctx = _ctx;
1273 : 1 : struct fio_file *f = ctx->u.moza.f;
1274 : : struct spdk_bdev *bdev;
1275 : :
1276 : 1 : bdev = spdk_bdev_get_by_name(f->file_name);
1277 [ - + ]: 1 : if (!bdev) {
1278 : 0 : SPDK_ERRLOG("Cannot get max open zones, no bdev with name: %s\n", f->file_name);
1279 : 0 : ctx->ret = -ENODEV;
1280 : : } else {
1281 : 1 : *ctx->u.moza.max_open_zones = spdk_bdev_get_max_open_zones(bdev);
1282 : 1 : ctx->ret = 0;
1283 : : }
1284 : :
1285 : 1 : spdk_fio_wake_oat_waiter(ctx);
1286 : 1 : }
1287 : :
1288 : : static int
1289 : 1 : spdk_fio_get_max_open_zones(struct thread_data *td, struct fio_file *f,
1290 : : unsigned int *max_open_zones)
1291 : : {
1292 : 1 : struct spdk_fio_oat_ctx ctx = { 0 };
1293 : :
1294 : 1 : ctx.u.moza.f = f;
1295 : 1 : ctx.u.moza.max_open_zones = max_open_zones;
1296 : :
1297 : 1 : spdk_fio_sync_run_oat(spdk_fio_get_max_open_zones_oat, &ctx);
1298 : :
1299 : 1 : return ctx.ret;
1300 : : }
1301 : : #endif
1302 : :
1303 : : static int
1304 : 339 : spdk_fio_handle_options(struct thread_data *td, struct fio_file *f, struct spdk_bdev *bdev)
1305 : : {
1306 : 339 : struct spdk_fio_options *fio_options = td->eo;
1307 : :
1308 [ + + + - ]: 339 : if (fio_options->initial_zone_reset && spdk_bdev_is_zoned(bdev)) {
1309 : : #if FIO_HAS_ZBD
1310 : 1 : int rc = spdk_fio_init(td);
1311 [ - + ]: 1 : if (rc) {
1312 : 0 : return rc;
1313 : : }
1314 : : /* offset used to indicate conventional zones that need to be skipped (reset not allowed) */
1315 : 1 : rc = spdk_fio_reset_zones(td->io_ops_data, f->engine_data, td->o.start_offset,
1316 : 1 : f->real_file_size - td->o.start_offset);
1317 [ - + ]: 1 : if (rc) {
1318 : 0 : spdk_fio_cleanup(td);
1319 : 0 : return rc;
1320 : : }
1321 : : #else
1322 : : SPDK_ERRLOG("fio version is too old to support zoned block devices\n");
1323 : : #endif
1324 : : }
1325 : :
1326 : 339 : return 0;
1327 : : }
1328 : :
1329 : : static int
1330 : 339 : spdk_fio_handle_options_per_target(struct thread_data *td, struct fio_file *f)
1331 : : {
1332 : 339 : struct spdk_fio_target *target = f->engine_data;
1333 : 339 : struct spdk_fio_options *fio_options = td->eo;
1334 : :
1335 [ + + + - ]: 339 : if (fio_options->zone_append && spdk_bdev_is_zoned(target->bdev)) {
1336 [ + - ]: 1 : if (spdk_bdev_io_type_supported(target->bdev, SPDK_BDEV_IO_TYPE_ZONE_APPEND)) {
1337 [ - + # # ]: 1 : SPDK_DEBUGLOG(fio_bdev, "Using zone appends instead of writes on: '%s'\n",
1338 : : f->file_name);
1339 : 1 : target->zone_append_enabled = true;
1340 : : } else {
1341 : 0 : SPDK_WARNLOG("Falling back to writes on: '%s' - bdev lacks zone append cmd\n",
1342 : : f->file_name);
1343 : : }
1344 : : }
1345 : :
1346 : 339 : return 0;
1347 : : }
1348 : :
1349 : : static struct fio_option options[] = {
1350 : : {
1351 : : .name = "spdk_conf",
1352 : : .lname = "SPDK configuration file",
1353 : : .type = FIO_OPT_STR_STORE,
1354 : : .off1 = offsetof(struct spdk_fio_options, conf),
1355 : : .help = "A SPDK JSON configuration file",
1356 : : .category = FIO_OPT_C_ENGINE,
1357 : : .group = FIO_OPT_G_INVALID,
1358 : : },
1359 : : {
1360 : : .name = "spdk_json_conf",
1361 : : .lname = "SPDK JSON configuration file",
1362 : : .type = FIO_OPT_STR_STORE,
1363 : : .off1 = offsetof(struct spdk_fio_options, json_conf),
1364 : : .help = "A SPDK JSON configuration file",
1365 : : .category = FIO_OPT_C_ENGINE,
1366 : : .group = FIO_OPT_G_INVALID,
1367 : : },
1368 : : {
1369 : : .name = "spdk_mem",
1370 : : .lname = "SPDK memory in MB",
1371 : : .type = FIO_OPT_INT,
1372 : : .off1 = offsetof(struct spdk_fio_options, mem_mb),
1373 : : .help = "Amount of memory in MB to allocate for SPDK",
1374 : : .category = FIO_OPT_C_ENGINE,
1375 : : .group = FIO_OPT_G_INVALID,
1376 : : },
1377 : : {
1378 : : .name = "spdk_single_seg",
1379 : : .lname = "SPDK switch to create just a single hugetlbfs file",
1380 : : .type = FIO_OPT_BOOL,
1381 : : .off1 = offsetof(struct spdk_fio_options, mem_single_seg),
1382 : : .help = "If set to 1, SPDK will use just a single hugetlbfs file",
1383 : : .def = "0",
1384 : : .category = FIO_OPT_C_ENGINE,
1385 : : .group = FIO_OPT_G_INVALID,
1386 : : },
1387 : : {
1388 : : .name = "log_flags",
1389 : : .lname = "log flags",
1390 : : .type = FIO_OPT_STR_STORE,
1391 : : .off1 = offsetof(struct spdk_fio_options, log_flags),
1392 : : .help = "SPDK log flags to enable",
1393 : : .category = FIO_OPT_C_ENGINE,
1394 : : .group = FIO_OPT_G_INVALID,
1395 : : },
1396 : : {
1397 : : .name = "initial_zone_reset",
1398 : : .lname = "Reset Zones on initialization",
1399 : : .type = FIO_OPT_INT,
1400 : : .off1 = offsetof(struct spdk_fio_options, initial_zone_reset),
1401 : : .def = "0",
1402 : : .help = "Reset Zones on initialization (0=disable, 1=Reset All Zones)",
1403 : : .category = FIO_OPT_C_ENGINE,
1404 : : .group = FIO_OPT_G_INVALID,
1405 : : },
1406 : : {
1407 : : .name = "zone_append",
1408 : : .lname = "Use zone append instead of write",
1409 : : .type = FIO_OPT_INT,
1410 : : .off1 = offsetof(struct spdk_fio_options, zone_append),
1411 : : .def = "0",
1412 : : .help = "Use zone append instead of write (1=zone append, 0=write)",
1413 : : .category = FIO_OPT_C_ENGINE,
1414 : : .group = FIO_OPT_G_INVALID,
1415 : : },
1416 : : {
1417 : : .name = "env_context",
1418 : : .lname = "Environment context options",
1419 : : .type = FIO_OPT_STR_STORE,
1420 : : .off1 = offsetof(struct spdk_fio_options, env_context),
1421 : : .help = "Opaque context for use of the env implementation",
1422 : : .category = FIO_OPT_C_ENGINE,
1423 : : .group = FIO_OPT_G_INVALID,
1424 : : },
1425 : : {
1426 : : .name = "spdk_rpc_listen_addr",
1427 : : .lname = "SPDK RPC listen address",
1428 : : .type = FIO_OPT_STR_STORE,
1429 : : .off1 = offsetof(struct spdk_fio_options, rpc_listen_addr),
1430 : : .help = "The address to listen the RPC operations",
1431 : : .category = FIO_OPT_C_ENGINE,
1432 : : .group = FIO_OPT_G_INVALID,
1433 : : },
1434 : : {
1435 : : .name = NULL,
1436 : : },
1437 : : };
1438 : :
1439 : : /* FIO imports this structure using dlsym */
1440 : : struct ioengine_ops ioengine = {
1441 : : .name = "spdk_bdev",
1442 : : .version = FIO_IOOPS_VERSION,
1443 : : .flags = FIO_RAWIO | FIO_NOEXTEND | FIO_NODISKUTIL | FIO_MEMALIGN | FIO_DISKLESSIO,
1444 : : .setup = spdk_fio_setup,
1445 : : .init = spdk_fio_init,
1446 : : /* .prep = unused, */
1447 : : .queue = spdk_fio_queue,
1448 : : /* .commit = unused, */
1449 : : .getevents = spdk_fio_getevents,
1450 : : .event = spdk_fio_event,
1451 : : /* .errdetails = unused, */
1452 : : /* .cancel = unused, */
1453 : : .cleanup = spdk_fio_cleanup,
1454 : : .open_file = spdk_fio_open,
1455 : : .close_file = spdk_fio_close,
1456 : : .invalidate = spdk_fio_invalidate,
1457 : : /* .unlink_file = unused, */
1458 : : /* .get_file_size = unused, */
1459 : : /* .terminate = unused, */
1460 : : .iomem_alloc = spdk_fio_iomem_alloc,
1461 : : .iomem_free = spdk_fio_iomem_free,
1462 : : .io_u_init = spdk_fio_io_u_init,
1463 : : .io_u_free = spdk_fio_io_u_free,
1464 : : #if FIO_HAS_ZBD
1465 : : .get_zoned_model = spdk_fio_get_zoned_model,
1466 : : .report_zones = spdk_fio_report_zones,
1467 : : .reset_wp = spdk_fio_reset_wp,
1468 : : #endif
1469 : : #if FIO_IOOPS_VERSION >= 30
1470 : : .get_max_open_zones = spdk_fio_get_max_open_zones,
1471 : : #endif
1472 : : .option_struct_size = sizeof(struct spdk_fio_options),
1473 : : .options = options,
1474 : : };
1475 : :
1476 : : static void fio_init
1477 : 47 : spdk_fio_register(void)
1478 : : {
1479 : 47 : register_ioengine(&ioengine);
1480 : 47 : }
1481 : :
1482 : : static void
1483 : 47 : spdk_fio_finish_env(void)
1484 : : {
1485 [ - + ]: 47 : pthread_mutex_lock(&g_init_mtx);
1486 : 47 : g_poll_loop = false;
1487 [ - + ]: 47 : pthread_cond_signal(&g_init_cond);
1488 [ - + ]: 47 : pthread_mutex_unlock(&g_init_mtx);
1489 : 47 : pthread_join(g_init_thread_id, NULL);
1490 : :
1491 : 47 : spdk_thread_lib_fini();
1492 : 47 : spdk_env_fini();
1493 : 47 : }
1494 : :
1495 : : static void fio_exit
1496 : 47 : spdk_fio_unregister(void)
1497 : : {
1498 [ + + + - ]: 47 : if (g_spdk_env_initialized) {
1499 : 47 : spdk_fio_finish_env();
1500 : 47 : g_spdk_env_initialized = false;
1501 : : }
1502 : 47 : unregister_ioengine(&ioengine);
1503 : 47 : }
1504 : :
1505 : 47 : SPDK_LOG_REGISTER_COMPONENT(fio_bdev)
|