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