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