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