Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2017 Intel Corporation.
3 : : * All rights reserved.
4 : : * Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
5 : : */
6 : :
7 : : #include "spdk/stdinc.h"
8 : :
9 : : #include "spdk/bdev.h"
10 : : #include "spdk/bdev_zone.h"
11 : : #include "spdk/accel.h"
12 : : #include "spdk/env.h"
13 : : #include "spdk/init.h"
14 : : #include "spdk/thread.h"
15 : : #include "spdk/log.h"
16 : : #include "spdk/string.h"
17 : : #include "spdk/queue.h"
18 : : #include "spdk/util.h"
19 : : #include "spdk/rpc.h"
20 : :
21 : : #include "spdk_internal/event.h"
22 : :
23 : : #include "config-host.h"
24 : : #include "fio.h"
25 : : #include "optgroup.h"
26 : :
27 : : #ifdef for_each_rw_ddir
28 : : #define FIO_HAS_ZBD (FIO_IOOPS_VERSION >= 26)
29 : : #else
30 : : #define FIO_HAS_ZBD (0)
31 : : #endif
32 : :
33 : : /* FreeBSD is missing CLOCK_MONOTONIC_RAW,
34 : : * so alternative is provided. */
35 : : #ifndef CLOCK_MONOTONIC_RAW /* Defined in glibc bits/time.h */
36 : : #define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
37 : : #endif
38 : :
39 : : struct spdk_fio_options {
40 : : void *pad;
41 : : char *conf;
42 : : char *json_conf;
43 : : char *env_context;
44 : : char *log_flags;
45 : : unsigned mem_mb;
46 : : int mem_single_seg;
47 : : int initial_zone_reset;
48 : : int zone_append;
49 : : char *rpc_listen_addr;
50 : : };
51 : :
52 : : struct spdk_fio_request {
53 : : struct io_u *io;
54 : : struct thread_data *td;
55 : : };
56 : :
57 : : struct spdk_fio_target {
58 : : struct spdk_bdev *bdev;
59 : : struct spdk_bdev_desc *desc;
60 : : struct spdk_io_channel *ch;
61 : : bool zone_append_enabled;
62 : :
63 : : TAILQ_ENTRY(spdk_fio_target) link;
64 : : };
65 : :
66 : : struct spdk_fio_thread {
67 : : struct thread_data *td; /* fio thread context */
68 : : struct spdk_thread *thread; /* spdk thread context */
69 : :
70 : : TAILQ_HEAD(, spdk_fio_target) targets;
71 : : bool failed; /* true if the thread failed to initialize */
72 : :
73 : : struct io_u **iocq; /* io completion queue */
74 : : unsigned int iocq_count; /* number of iocq entries filled by last getevents */
75 : : unsigned int iocq_size; /* number of iocq entries allocated */
76 : :
77 : : TAILQ_ENTRY(spdk_fio_thread) link;
78 : : };
79 : :
80 : : struct spdk_fio_zone_cb_arg {
81 : : struct spdk_fio_target *target;
82 : : struct spdk_bdev_zone_info *spdk_zones;
83 : : int completed;
84 : : uint64_t offset_blocks;
85 : : struct zbd_zone *fio_zones;
86 : : unsigned int nr_zones;
87 : : };
88 : :
89 : : /* On App Thread (oat) context used for making sync calls from async calls. */
90 : : struct spdk_fio_oat_ctx {
91 : : union {
92 : : struct spdk_fio_setup_args {
93 : : struct thread_data *td;
94 : : } sa;
95 : : struct spdk_fio_bdev_get_zoned_model_args {
96 : : struct fio_file *f;
97 : : enum zbd_zoned_model *model;
98 : : } zma;
99 : : struct spdk_fio_bdev_get_max_open_zones_args {
100 : : struct fio_file *f;
101 : : unsigned int *max_open_zones;
102 : : } moza;
103 : : } u;
104 : : pthread_mutex_t mutex;
105 : : pthread_cond_t cond;
106 : : int ret;
107 : : };
108 : :
109 : : static bool g_spdk_env_initialized = false;
110 : : static const char *g_json_config_file = NULL;
111 : : static const char *g_rpc_listen_addr = 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 : 282 : spdk_fio_sync_run_oat(void (*msg_fn)(void *), struct spdk_fio_oat_ctx *ctx)
135 : : {
136 [ - + ]: 282 : assert(!spdk_thread_is_app_thread(NULL));
137 : :
138 [ - + ]: 282 : pthread_mutex_init(&ctx->mutex, NULL);
139 [ - + ]: 282 : pthread_cond_init(&ctx->cond, NULL);
140 [ - + ]: 282 : pthread_mutex_lock(&ctx->mutex);
141 : :
142 : 282 : 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 [ - + ]: 282 : pthread_mutex_lock(&g_init_mtx);
146 [ - + ]: 282 : pthread_cond_signal(&g_init_cond);
147 [ - + ]: 282 : pthread_mutex_unlock(&g_init_mtx);
148 : :
149 : : /* Wait for msg_fn() to call spdk_fio_wake_oat_waiter() */
150 [ - + - + ]: 282 : pthread_cond_wait(&ctx->cond, &ctx->mutex);
151 [ - + ]: 282 : pthread_mutex_unlock(&ctx->mutex);
152 : :
153 [ - + ]: 282 : pthread_mutex_destroy(&ctx->mutex);
154 [ - + ]: 282 : pthread_cond_destroy(&ctx->cond);
155 : 282 : }
156 : :
157 : : static void
158 : 282 : spdk_fio_wake_oat_waiter(struct spdk_fio_oat_ctx *ctx)
159 : : {
160 [ - + ]: 282 : pthread_mutex_lock(&ctx->mutex);
161 [ - + ]: 282 : pthread_cond_signal(&ctx->cond);
162 [ - + ]: 282 : pthread_mutex_unlock(&ctx->mutex);
163 : 282 : }
164 : :
165 : : static int
166 : 325 : spdk_fio_schedule_thread(struct spdk_thread *thread)
167 : : {
168 : : struct spdk_fio_thread *fio_thread;
169 : :
170 [ + + + - ]: 325 : if (g_internal_thread) {
171 : : /* Do nothing. */
172 : 325 : return 0;
173 : : }
174 : :
175 : 0 : fio_thread = spdk_thread_get_ctx(thread);
176 : :
177 [ # # ]: 0 : pthread_mutex_lock(&g_init_mtx);
178 : 0 : TAILQ_INSERT_TAIL(&g_threads, fio_thread, link);
179 [ # # ]: 0 : pthread_mutex_unlock(&g_init_mtx);
180 : :
181 : 0 : return 0;
182 : : }
183 : :
184 : : static int
185 : 325 : spdk_fio_init_thread(struct thread_data *td)
186 : : {
187 : : struct spdk_fio_thread *fio_thread;
188 : : struct spdk_thread *thread;
189 : :
190 : 325 : g_internal_thread = true;
191 : 325 : thread = spdk_thread_create("fio_thread", NULL);
192 : 325 : g_internal_thread = false;
193 [ - + ]: 325 : if (!thread) {
194 : 0 : SPDK_ERRLOG("failed to allocate thread\n");
195 : 0 : return -1;
196 : : }
197 : :
198 : 325 : fio_thread = spdk_thread_get_ctx(thread);
199 : 325 : fio_thread->td = td;
200 : 325 : fio_thread->thread = thread;
201 : 325 : td->io_ops_data = fio_thread;
202 : :
203 : 325 : spdk_set_thread(thread);
204 : :
205 : 325 : fio_thread->iocq_size = td->o.iodepth;
206 : 325 : fio_thread->iocq = calloc(fio_thread->iocq_size, sizeof(struct io_u *));
207 [ - + ]: 325 : assert(fio_thread->iocq != NULL);
208 : :
209 : 325 : TAILQ_INIT(&fio_thread->targets);
210 : :
211 : 325 : return 0;
212 : : }
213 : :
214 : : static void
215 : 325 : spdk_fio_bdev_close_targets(void *arg)
216 : : {
217 : 325 : struct spdk_fio_thread *fio_thread = arg;
218 : : struct spdk_fio_target *target, *tmp;
219 : :
220 [ + + ]: 634 : TAILQ_FOREACH_SAFE(target, &fio_thread->targets, link, tmp) {
221 [ + + ]: 309 : TAILQ_REMOVE(&fio_thread->targets, target, link);
222 : 309 : spdk_put_io_channel(target->ch);
223 : 309 : spdk_bdev_close(target->desc);
224 : 309 : free(target);
225 : : }
226 : 325 : }
227 : :
228 : : static void
229 : 325 : spdk_fio_cleanup_thread(struct spdk_fio_thread *fio_thread)
230 : : {
231 : 325 : spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_close_targets, fio_thread);
232 : :
233 [ - + ]: 325 : pthread_mutex_lock(&g_init_mtx);
234 : 325 : TAILQ_INSERT_TAIL(&g_threads, fio_thread, link);
235 [ - + ]: 325 : pthread_mutex_unlock(&g_init_mtx);
236 : 325 : }
237 : :
238 : : static void
239 : 1836728 : spdk_fio_calc_timeout(struct spdk_fio_thread *fio_thread, struct timespec *ts)
240 : : {
241 : : uint64_t timeout, now;
242 : :
243 [ + + ]: 1836728 : if (spdk_thread_has_active_pollers(fio_thread->thread)) {
244 : 1812154 : return;
245 : : }
246 : :
247 : 24574 : timeout = spdk_thread_next_poller_expiration(fio_thread->thread);
248 : 24574 : now = spdk_get_ticks();
249 : :
250 [ + + ]: 24574 : if (timeout == 0) {
251 : 123 : timeout = now + (SPDK_FIO_POLLING_TIMEOUT * spdk_get_ticks_hz()) / SPDK_SEC_TO_NSEC;
252 : : }
253 : :
254 [ + + ]: 24574 : if (timeout > now) {
255 [ - + ]: 24543 : timeout = ((timeout - now) * SPDK_SEC_TO_NSEC) / spdk_get_ticks_hz() +
256 : 24543 : ts->tv_sec * SPDK_SEC_TO_NSEC + ts->tv_nsec;
257 : :
258 : 24543 : ts->tv_sec = timeout / SPDK_SEC_TO_NSEC;
259 : 24543 : ts->tv_nsec = timeout % SPDK_SEC_TO_NSEC;
260 : : }
261 : : }
262 : :
263 : : static void
264 : 45 : spdk_fio_bdev_init_done(int rc, void *cb_arg)
265 : : {
266 : 45 : *(bool *)cb_arg = true;
267 : :
268 [ - + ]: 45 : if (g_rpc_listen_addr != NULL) {
269 [ # # ]: 0 : if (spdk_rpc_initialize(g_rpc_listen_addr, NULL) == 0) {
270 : 0 : spdk_rpc_set_state(SPDK_RPC_RUNTIME);
271 : : }
272 : : }
273 : 45 : }
274 : :
275 : : static void
276 : 45 : spdk_fio_bdev_init_start(void *arg)
277 : : {
278 : 45 : bool *done = arg;
279 : :
280 : 45 : spdk_subsystem_init_from_json_config(g_json_config_file, SPDK_DEFAULT_RPC_ADDR,
281 : : spdk_fio_bdev_init_done, done, true);
282 : 45 : }
283 : :
284 : : static void
285 : 45 : spdk_fio_bdev_fini_done(void *cb_arg)
286 : : {
287 : 45 : *(bool *)cb_arg = true;
288 : :
289 : 45 : spdk_rpc_finish();
290 : 45 : }
291 : :
292 : : static void
293 : 45 : spdk_fio_bdev_fini_start(void *arg)
294 : : {
295 : 45 : bool *done = arg;
296 : :
297 : 45 : spdk_subsystem_fini(spdk_fio_bdev_fini_done, done);
298 : 45 : }
299 : :
300 : : static void *
301 : 45 : spdk_init_thread_poll(void *arg)
302 : : {
303 : 45 : struct spdk_fio_options *eo = arg;
304 : : struct spdk_fio_thread *fio_thread;
305 : : struct spdk_fio_thread *thread, *tmp;
306 : 11 : struct spdk_env_opts opts;
307 : 11 : bool done;
308 : : int rc;
309 : 11 : struct timespec ts;
310 : 45 : struct thread_data td = {};
311 : :
312 : : /* Create a dummy thread data for use on the initialization thread. */
313 : 45 : td.o.iodepth = 32;
314 : 45 : td.eo = eo;
315 : :
316 : : /* Parse the SPDK configuration file */
317 : 45 : eo = arg;
318 : :
319 [ - + - - ]: 45 : if (eo->conf && eo->json_conf) {
320 : 0 : SPDK_ERRLOG("Cannot provide two types of configuration files\n");
321 : 0 : rc = EINVAL;
322 : 0 : goto err_exit;
323 [ - + - - ]: 45 : } else if (eo->conf && strlen(eo->conf)) {
324 : 0 : g_json_config_file = eo->conf;
325 [ + - + - ]: 45 : } else if (eo->json_conf && strlen(eo->json_conf)) {
326 : 45 : g_json_config_file = eo->json_conf;
327 : : } else {
328 : 0 : SPDK_ERRLOG("No configuration file provided\n");
329 : 0 : rc = EINVAL;
330 : 0 : goto err_exit;
331 : : }
332 : :
333 : : /* Initialize the RPC listen address */
334 [ - + ]: 45 : if (eo->rpc_listen_addr) {
335 : 0 : g_rpc_listen_addr = eo->rpc_listen_addr;
336 : : }
337 : :
338 : : /* Initialize the environment library */
339 : 45 : opts.opts_size = sizeof(opts);
340 : 45 : spdk_env_opts_init(&opts);
341 : 45 : opts.name = "fio";
342 : :
343 [ + + ]: 45 : if (eo->mem_mb) {
344 : 4 : opts.mem_size = eo->mem_mb;
345 : : }
346 : 45 : opts.hugepage_single_segments = eo->mem_single_seg;
347 [ - + ]: 45 : if (eo->env_context) {
348 : 0 : opts.env_context = eo->env_context;
349 : : }
350 : :
351 [ - + ]: 45 : if (spdk_env_init(&opts) < 0) {
352 : 0 : SPDK_ERRLOG("Unable to initialize SPDK env\n");
353 : 0 : rc = EINVAL;
354 : 0 : goto err_exit;
355 : : }
356 : 45 : spdk_unaffinitize_thread();
357 : :
358 [ - + ]: 45 : if (eo->log_flags) {
359 [ # # ]: 0 : char *tok = strtok(eo->log_flags, ",");
360 : : do {
361 : 0 : rc = spdk_log_set_flag(tok);
362 [ # # ]: 0 : if (rc < 0) {
363 : 0 : SPDK_ERRLOG("unknown spdk log flag %s\n", tok);
364 : 0 : rc = EINVAL;
365 : 0 : goto err_exit;
366 : : }
367 [ # # # # ]: 0 : } while ((tok = strtok(NULL, ",")) != NULL);
368 : : #ifdef DEBUG
369 : 0 : spdk_log_set_print_level(SPDK_LOG_DEBUG);
370 : : #endif
371 : : }
372 : :
373 : 45 : spdk_thread_lib_init(spdk_fio_schedule_thread, sizeof(struct spdk_fio_thread));
374 : :
375 : : /* Create an SPDK thread temporarily */
376 : 45 : rc = spdk_fio_init_thread(&td);
377 [ - + ]: 45 : if (rc < 0) {
378 : 0 : SPDK_ERRLOG("Failed to create initialization thread\n");
379 : 0 : goto err_exit;
380 : : }
381 : :
382 : 45 : fio_thread = td.io_ops_data;
383 : :
384 : : /* Initialize the bdev layer */
385 : 45 : done = false;
386 : 45 : spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_init_start, &done);
387 : :
388 : : do {
389 : 12301722 : spdk_fio_poll_thread(fio_thread);
390 [ + + + + ]: 12301722 : } while (!done);
391 : :
392 : : /*
393 : : * Continue polling until there are no more events.
394 : : * This handles any final events posted by pollers.
395 : : */
396 [ - + ]: 45 : while (spdk_fio_poll_thread(fio_thread) > 0) {};
397 : :
398 : : /* Set condition variable */
399 [ - + ]: 45 : pthread_mutex_lock(&g_init_mtx);
400 [ - + ]: 45 : pthread_cond_signal(&g_init_cond);
401 : :
402 [ - + ]: 45 : pthread_mutex_unlock(&g_init_mtx);
403 : :
404 [ + + + + ]: 4056156 : while (g_poll_loop) {
405 : 4056111 : spdk_fio_poll_thread(fio_thread);
406 : :
407 [ - + ]: 4056111 : pthread_mutex_lock(&g_init_mtx);
408 [ + + ]: 4056111 : if (!TAILQ_EMPTY(&g_threads)) {
409 [ + + ]: 15460550 : TAILQ_FOREACH_SAFE(thread, &g_threads, link, tmp) {
410 [ - + ]: 13241167 : if (spdk_thread_is_exited(thread->thread)) {
411 [ # # ]: 0 : TAILQ_REMOVE(&g_threads, thread, link);
412 : 0 : free(thread->iocq);
413 : 0 : spdk_thread_destroy(thread->thread);
414 : : } else {
415 : 13241167 : spdk_fio_poll_thread(thread);
416 : : }
417 : : }
418 : :
419 : : /* If there are exiting threads to poll, don't sleep. */
420 [ - + ]: 2219383 : pthread_mutex_unlock(&g_init_mtx);
421 : 2219383 : continue;
422 : : }
423 : :
424 : : /* Figure out how long to sleep. */
425 [ - + ]: 1836728 : clock_gettime(CLOCK_MONOTONIC, &ts);
426 : 1836728 : spdk_fio_calc_timeout(fio_thread, &ts);
427 : :
428 [ - + - + : 1836728 : rc = pthread_cond_timedwait(&g_init_cond, &g_init_mtx, &ts);
- + ]
429 [ - + ]: 1836728 : pthread_mutex_unlock(&g_init_mtx);
430 : :
431 [ + + - + ]: 1836728 : if (rc != 0 && rc != ETIMEDOUT) {
432 : 0 : break;
433 : : }
434 : : }
435 : :
436 : 45 : spdk_fio_cleanup_thread(fio_thread);
437 : :
438 : : /* Finalize the bdev layer */
439 : 45 : done = false;
440 : 45 : spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_fini_start, &done);
441 : :
442 : : do {
443 : 1076845 : spdk_fio_poll_thread(fio_thread);
444 : :
445 [ + + ]: 7334617 : TAILQ_FOREACH_SAFE(thread, &g_threads, link, tmp) {
446 : 6257772 : spdk_fio_poll_thread(thread);
447 : : }
448 [ + + + + ]: 1076845 : } while (!done);
449 : :
450 : : /* Now exit all the threads */
451 [ + + ]: 370 : TAILQ_FOREACH(thread, &g_threads, link) {
452 : 325 : spdk_set_thread(thread->thread);
453 : 325 : spdk_thread_exit(thread->thread);
454 : 325 : spdk_set_thread(NULL);
455 : : }
456 : :
457 : : /* And wait for them to gracefully exit */
458 [ + + ]: 135 : while (!TAILQ_EMPTY(&g_threads)) {
459 [ + + ]: 740 : TAILQ_FOREACH_SAFE(thread, &g_threads, link, tmp) {
460 [ + + ]: 650 : if (spdk_thread_is_exited(thread->thread)) {
461 [ + + ]: 325 : TAILQ_REMOVE(&g_threads, thread, link);
462 : 325 : free(thread->iocq);
463 : 325 : spdk_thread_destroy(thread->thread);
464 : : } else {
465 : 325 : spdk_thread_poll(thread->thread, 0, 0);
466 : : }
467 : : }
468 : : }
469 : :
470 : 45 : pthread_exit(NULL);
471 : :
472 : 0 : err_exit:
473 : 0 : exit(rc);
474 : : return NULL;
475 : : }
476 : :
477 : : static int
478 : 45 : spdk_fio_init_env(struct thread_data *td)
479 : : {
480 : 11 : pthread_condattr_t attr;
481 : 45 : int rc = -1;
482 : :
483 [ - + - + ]: 45 : if (pthread_condattr_init(&attr)) {
484 : 0 : SPDK_ERRLOG("Unable to initialize condition variable\n");
485 : 0 : return -1;
486 : : }
487 : :
488 [ - + - + ]: 45 : if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) {
489 : 0 : SPDK_ERRLOG("Unable to initialize condition variable\n");
490 : 0 : goto out;
491 : : }
492 : :
493 [ - + - + ]: 45 : if (pthread_cond_init(&g_init_cond, &attr)) {
494 : 0 : SPDK_ERRLOG("Unable to initialize condition variable\n");
495 : 0 : 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 [ - + - + ]: 45 : rc = pthread_create(&g_init_thread_id, NULL, &spdk_init_thread_poll, td->eo);
503 [ - + ]: 45 : if (rc != 0) {
504 : 0 : 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 [ - + ]: 45 : pthread_mutex_lock(&g_init_mtx);
509 [ - + - + ]: 45 : pthread_cond_wait(&g_init_cond, &g_init_mtx);
510 [ - + ]: 45 : pthread_mutex_unlock(&g_init_mtx);
511 : 45 : out:
512 [ - + ]: 45 : pthread_condattr_destroy(&attr);
513 : 45 : return rc;
514 : : }
515 : :
516 : : static bool
517 : 0 : fio_redirected_to_dev_null(void)
518 : : {
519 : 0 : char path[PATH_MAX] = "";
520 : : ssize_t ret;
521 : :
522 : 0 : ret = readlink("/proc/self/fd/1", path, sizeof(path));
523 : :
524 [ # # # # ]: 0 : if (ret == -1 || strcmp(path, "/dev/null") != 0) {
525 : 0 : return false;
526 : : }
527 : :
528 : 0 : ret = readlink("/proc/self/fd/2", path, sizeof(path));
529 : :
530 [ # # # # ]: 0 : if (ret == -1 || strcmp(path, "/dev/null") != 0) {
531 : 0 : return false;
532 : : }
533 : :
534 : 0 : return true;
535 : : }
536 : :
537 : : static int
538 : 562 : spdk_fio_init_spdk_env(struct thread_data *td)
539 : : {
540 : : static pthread_mutex_t setup_lock = PTHREAD_MUTEX_INITIALIZER;
541 : :
542 [ - + ]: 562 : pthread_mutex_lock(&setup_lock);
543 [ + + + + ]: 562 : if (!g_spdk_env_initialized) {
544 [ - + ]: 45 : if (spdk_fio_init_env(td)) {
545 [ # # ]: 0 : pthread_mutex_unlock(&setup_lock);
546 : 0 : SPDK_ERRLOG("failed to initialize\n");
547 : 0 : return -1;
548 : : }
549 : :
550 : 45 : g_spdk_env_initialized = true;
551 : : }
552 [ - + ]: 562 : pthread_mutex_unlock(&setup_lock);
553 : :
554 : 562 : 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 : 280 : spdk_fio_setup(struct thread_data *td)
566 : : {
567 : 280 : 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 [ - + - + : 280 : if (is_backend && !fio_redirected_to_dev_null()) {
- - ]
577 : 0 : char buf[1024];
578 [ # # ]: 0 : snprintf(buf, sizeof(buf),
579 : : "SPDK FIO plugin is in daemon mode, but stdout/stderr "
580 : : "aren't redirected to /dev/null. Aborting.");
581 : 0 : fio_server_text_output(FIO_LOG_ERR, buf, sizeof(buf));
582 : 0 : return -1;
583 : : }
584 : :
585 [ - + ]: 280 : if (!td->o.use_thread) {
586 : 0 : SPDK_ERRLOG("must set thread=1 when using spdk plugin\n");
587 : 0 : return -1;
588 : : }
589 : :
590 [ - + ]: 280 : if (spdk_fio_init_spdk_env(td) != 0) {
591 : 0 : return -1;
592 : : }
593 : :
594 : 280 : ctx.u.sa.td = td;
595 : 280 : spdk_fio_sync_run_oat(spdk_fio_setup_oat, &ctx);
596 : 280 : return ctx.ret;
597 : : }
598 : :
599 : : static int
600 : 8 : _spdk_fio_add_file(void *ctx, struct spdk_bdev *bdev)
601 : : {
602 : 8 : struct thread_data *td = ctx;
603 : :
604 : 8 : add_file(td, spdk_bdev_get_name(bdev), 0, 1);
605 : 8 : return 0;
606 : : }
607 : :
608 : : static void
609 : 280 : spdk_fio_setup_oat(void *_ctx)
610 : : {
611 : 280 : struct spdk_fio_oat_ctx *ctx = _ctx;
612 : 280 : struct thread_data *td = ctx->u.sa.td;
613 : : unsigned int i;
614 : : struct fio_file *f;
615 : :
616 [ + + - + : 280 : if (td->o.nr_files == 1 && strcmp(td->files[0]->file_name, "*") == 0) {
+ + ]
617 : : /* add all available bdevs as fio targets */
618 : 1 : spdk_for_each_bdev_leaf(td, _spdk_fio_add_file);
619 : : }
620 : :
621 [ + - + + : 590 : for_each_file(td, f, i) {
+ - ]
622 : : struct spdk_bdev *bdev;
623 : :
624 [ - + + + ]: 310 : 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 : 1 : f->real_file_size = 0;
629 : 1 : continue;
630 : : }
631 : :
632 : 309 : bdev = spdk_bdev_get_by_name(f->file_name);
633 [ - + ]: 309 : if (!bdev) {
634 : 0 : SPDK_ERRLOG("Unable to find bdev with name %s\n", f->file_name);
635 : 0 : ctx->ret = -1;
636 : 0 : goto out;
637 : : }
638 : :
639 : 587 : f->real_file_size = spdk_bdev_get_num_blocks(bdev) *
640 : 309 : spdk_bdev_get_block_size(bdev);
641 : 309 : f->filetype = FIO_TYPE_BLOCK;
642 : 309 : fio_file_set_size_known(f);
643 : :
644 : 309 : ctx->ret = spdk_fio_handle_options(td, f, bdev);
645 [ - + ]: 309 : if (ctx->ret) {
646 : 0 : goto out;
647 : : }
648 : : }
649 : :
650 : 280 : ctx->ret = 0;
651 : 280 : out:
652 : 280 : spdk_fio_wake_oat_waiter(ctx);
653 : 280 : }
654 : :
655 : : static void
656 : 0 : fio_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
657 : : void *event_ctx)
658 : : {
659 : 0 : SPDK_WARNLOG("Unsupported bdev event: type %d\n", type);
660 : 0 : }
661 : :
662 : : static void
663 : 280 : spdk_fio_bdev_open(void *arg)
664 : : {
665 : 280 : 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 : 280 : fio_thread = td->io_ops_data;
672 : :
673 [ + - + + : 590 : for_each_file(td, f, i) {
+ - ]
674 : : struct spdk_fio_target *target;
675 : :
676 [ - + + + ]: 310 : if (strcmp(f->file_name, "*") == 0) {
677 : 1 : continue;
678 : : }
679 : :
680 : 309 : target = calloc(1, sizeof(*target));
681 [ - + ]: 309 : if (!target) {
682 : 0 : SPDK_ERRLOG("Unable to allocate memory for I/O target.\n");
683 : 0 : fio_thread->failed = true;
684 : 0 : return;
685 : : }
686 : :
687 : 309 : rc = spdk_bdev_open_ext(f->file_name, true, fio_bdev_event_cb, NULL,
688 : : &target->desc);
689 [ - + ]: 309 : if (rc) {
690 : 0 : SPDK_ERRLOG("Unable to open bdev %s\n", f->file_name);
691 : 0 : free(target);
692 : 0 : fio_thread->failed = true;
693 : 0 : return;
694 : : }
695 : :
696 : 309 : target->bdev = spdk_bdev_desc_get_bdev(target->desc);
697 : :
698 : 309 : target->ch = spdk_bdev_get_io_channel(target->desc);
699 [ - + ]: 309 : if (!target->ch) {
700 : 0 : SPDK_ERRLOG("Unable to get I/O channel for bdev.\n");
701 : 0 : spdk_bdev_close(target->desc);
702 : 0 : free(target);
703 : 0 : fio_thread->failed = true;
704 : 0 : return;
705 : : }
706 : :
707 : 309 : f->engine_data = target;
708 : :
709 : 309 : rc = spdk_fio_handle_options_per_target(td, f);
710 [ - + ]: 309 : if (rc) {
711 : 0 : SPDK_ERRLOG("Failed to handle options for: %s\n", f->file_name);
712 : 0 : f->engine_data = NULL;
713 : 0 : spdk_put_io_channel(target->ch);
714 : 0 : spdk_bdev_close(target->desc);
715 : 0 : free(target);
716 : 0 : fio_thread->failed = true;
717 : 0 : return;
718 : : }
719 : :
720 : 309 : 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 : 282 : spdk_fio_init(struct thread_data *td)
735 : : {
736 : : struct spdk_fio_thread *fio_thread;
737 : : int rc;
738 : :
739 [ - + ]: 282 : if (spdk_fio_init_spdk_env(td) != 0) {
740 : 0 : return -1;
741 : : }
742 : :
743 : : /* If thread has already been initialized, do nothing. */
744 [ + + ]: 282 : if (td->io_ops_data) {
745 : 2 : return 0;
746 : : }
747 : :
748 : 280 : rc = spdk_fio_init_thread(td);
749 [ - + ]: 280 : if (rc) {
750 : 0 : return rc;
751 : : }
752 : :
753 : 280 : fio_thread = td->io_ops_data;
754 [ - + ]: 280 : assert(fio_thread);
755 : 280 : fio_thread->failed = false;
756 : :
757 : 280 : spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_open, td);
758 : :
759 [ + + ]: 568 : while (spdk_fio_poll_thread(fio_thread) > 0) {}
760 : :
761 [ - + - + ]: 280 : if (fio_thread->failed) {
762 : 0 : return -1;
763 : : }
764 : :
765 : 280 : return 0;
766 : : }
767 : :
768 : : static void
769 : 280 : spdk_fio_cleanup(struct thread_data *td)
770 : : {
771 : 280 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
772 : :
773 : 280 : spdk_fio_cleanup_thread(fio_thread);
774 : 280 : td->io_ops_data = NULL;
775 : 280 : }
776 : :
777 : : static int
778 : 5086 : spdk_fio_open(struct thread_data *td, struct fio_file *f)
779 : : {
780 : :
781 : 5086 : return 0;
782 : : }
783 : :
784 : : static int
785 : 5084 : spdk_fio_close(struct thread_data *td, struct fio_file *f)
786 : : {
787 : 5084 : return 0;
788 : : }
789 : :
790 : : static int
791 : 278 : spdk_fio_iomem_alloc(struct thread_data *td, size_t total_mem)
792 : : {
793 : 278 : td->orig_buffer = spdk_dma_zmalloc(total_mem, 0x1000, NULL);
794 : 278 : return td->orig_buffer == NULL;
795 : : }
796 : :
797 : : static void
798 : 280 : spdk_fio_iomem_free(struct thread_data *td)
799 : : {
800 : 280 : spdk_dma_free(td->orig_buffer);
801 : 280 : }
802 : :
803 : : static int
804 : 4707 : spdk_fio_io_u_init(struct thread_data *td, struct io_u *io_u)
805 : : {
806 : : struct spdk_fio_request *fio_req;
807 : :
808 : 4707 : io_u->engine_data = NULL;
809 : :
810 : 4707 : fio_req = calloc(1, sizeof(*fio_req));
811 [ - + ]: 4707 : if (fio_req == NULL) {
812 : 0 : return 1;
813 : : }
814 : 4707 : fio_req->io = io_u;
815 : 4707 : fio_req->td = td;
816 : :
817 : 4707 : io_u->engine_data = fio_req;
818 : :
819 : 4707 : return 0;
820 : : }
821 : :
822 : : static void
823 : 4705 : spdk_fio_io_u_free(struct thread_data *td, struct io_u *io_u)
824 : : {
825 : 4705 : struct spdk_fio_request *fio_req = io_u->engine_data;
826 : :
827 [ + - ]: 4705 : if (fio_req) {
828 [ - + ]: 4705 : assert(fio_req->io == io_u);
829 : 4705 : free(fio_req);
830 : 4705 : io_u->engine_data = NULL;
831 : : }
832 : 4705 : }
833 : :
834 : : static void
835 : 34334300 : spdk_fio_completion_cb(struct spdk_bdev_io *bdev_io,
836 : : bool success,
837 : : void *cb_arg)
838 : : {
839 : 34334300 : struct spdk_fio_request *fio_req = cb_arg;
840 : 34334300 : struct thread_data *td = fio_req->td;
841 : 34334300 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
842 : :
843 [ - + ]: 34334300 : assert(fio_thread->iocq_count < fio_thread->iocq_size);
844 [ + - ]: 34334300 : fio_req->io->error = success ? 0 : EIO;
845 : 34334300 : fio_thread->iocq[fio_thread->iocq_count++] = fio_req->io;
846 : :
847 : 34334300 : spdk_bdev_free_io(bdev_io);
848 : 34334300 : }
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 : 252331 : 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 : 252331 : uint32_t block_size = spdk_bdev_get_block_size(bdev);
861 [ # # ]: 252331 : *zone_start = spdk_bdev_get_zone_id(bdev, offset_bytes / block_size);
862 [ # # ]: 252331 : *num_blocks = num_bytes / block_size;
863 [ # # # # ]: 252331 : return (offset_bytes % block_size) | (num_bytes % block_size);
864 : : }
865 : :
866 : : static fio_q_status_t
867 : 34334300 : spdk_fio_queue(struct thread_data *td, struct io_u *io_u)
868 : : {
869 : 34334300 : int rc = 1;
870 : 34334300 : struct spdk_fio_request *fio_req = io_u->engine_data;
871 : 34334300 : struct spdk_fio_target *target = io_u->file->engine_data;
872 : :
873 [ - + ]: 34334300 : assert(fio_req->td == td);
874 : :
875 [ - + ]: 34334300 : if (!target) {
876 : 0 : SPDK_ERRLOG("Unable to look up correct I/O target.\n");
877 : 0 : fio_req->io->error = ENODEV;
878 : 0 : return FIO_Q_COMPLETED;
879 : : }
880 : :
881 [ + + + - : 34334300 : switch (io_u->ddir) {
- ]
882 : 7336273 : case DDIR_READ:
883 : 7336273 : rc = spdk_bdev_read(target->desc, target->ch,
884 : 7336273 : io_u->buf, io_u->offset, io_u->xfer_buflen,
885 : : spdk_fio_completion_cb, fio_req);
886 : 7336273 : break;
887 : 17833717 : case DDIR_WRITE:
888 [ + + + - ]: 17833717 : if (!target->zone_append_enabled) {
889 : 17581386 : rc = spdk_bdev_write(target->desc, target->ch,
890 : 17581386 : io_u->buf, io_u->offset, io_u->xfer_buflen,
891 : : spdk_fio_completion_cb, fio_req);
892 : : } else {
893 : 0 : uint64_t zone_start, num_blocks;
894 [ - + ]: 252331 : if (spdk_fio_zone_bytes_to_blocks(target->bdev, io_u->offset, &zone_start,
895 : 252331 : io_u->xfer_buflen, &num_blocks) != 0) {
896 : 0 : rc = -EINVAL;
897 : 0 : break;
898 : : }
899 : 252331 : 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 : 17833717 : break;
904 : 9164310 : case DDIR_TRIM:
905 : 9164310 : rc = spdk_bdev_unmap(target->desc, target->ch,
906 : 9164310 : io_u->offset, io_u->xfer_buflen,
907 : : spdk_fio_completion_cb, fio_req);
908 : 9164310 : break;
909 : 0 : case DDIR_SYNC:
910 : 0 : rc = spdk_bdev_flush(target->desc, target->ch,
911 : 0 : io_u->offset, io_u->xfer_buflen,
912 : : spdk_fio_completion_cb, fio_req);
913 : 0 : break;
914 : 0 : default:
915 : 0 : assert(false);
916 : : break;
917 : : }
918 : :
919 [ - + ]: 34334300 : if (rc == -ENOMEM) {
920 : 0 : return FIO_Q_BUSY;
921 : : }
922 : :
923 [ - + ]: 34334300 : if (rc != 0) {
924 : 0 : fio_req->io->error = abs(rc);
925 : 0 : return FIO_Q_COMPLETED;
926 : : }
927 : :
928 : 34334300 : return FIO_Q_QUEUED;
929 : : }
930 : :
931 : : static struct io_u *
932 : 34334300 : spdk_fio_event(struct thread_data *td, int event)
933 : : {
934 : 34334300 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
935 : :
936 [ - + ]: 34334300 : assert(event >= 0);
937 [ - + ]: 34334300 : assert((unsigned)event < fio_thread->iocq_count);
938 : 34334300 : return fio_thread->iocq[event];
939 : : }
940 : :
941 : : static size_t
942 : 205154307 : spdk_fio_poll_thread(struct spdk_fio_thread *fio_thread)
943 : : {
944 : 205154307 : return spdk_thread_poll(fio_thread->thread, 0, 0);
945 : : }
946 : :
947 : : static int
948 : 5165576 : spdk_fio_getevents(struct thread_data *td, unsigned int min,
949 : : unsigned int max, const struct timespec *t)
950 : : {
951 : 5165576 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
952 : 2880323 : struct timespec t0, t1;
953 : 5165576 : uint64_t timeout = 0;
954 : :
955 [ - + ]: 5165576 : if (t) {
956 : 0 : timeout = t->tv_sec * SPDK_SEC_TO_NSEC + t->tv_nsec;
957 [ # # ]: 0 : clock_gettime(CLOCK_MONOTONIC_RAW, &t0);
958 : : }
959 : :
960 : 5165576 : fio_thread->iocq_count = 0;
961 : :
962 : : for (;;) {
963 : 167825546 : spdk_fio_poll_thread(fio_thread);
964 : :
965 [ + + ]: 167825546 : if (fio_thread->iocq_count >= min) {
966 : 5165576 : return fio_thread->iocq_count;
967 : : }
968 : :
969 [ - + ]: 162659970 : if (t) {
970 [ # # ]: 0 : clock_gettime(CLOCK_MONOTONIC_RAW, &t1);
971 : 0 : uint64_t elapse = ((t1.tv_sec - t0.tv_sec) * SPDK_SEC_TO_NSEC)
972 : 0 : + t1.tv_nsec - t0.tv_nsec;
973 [ # # ]: 0 : if (elapse > timeout) {
974 : 0 : break;
975 : : }
976 : : }
977 : : }
978 : :
979 : 0 : return fio_thread->iocq_count;
980 : : }
981 : :
982 : : static int
983 : 0 : 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 : 0 : return 0;
987 : : }
988 : :
989 : : #if FIO_HAS_ZBD
990 : : /* Runs on app thread (oat) */
991 : : static void
992 : 1 : spdk_fio_get_zoned_model_oat(void *arg)
993 : : {
994 : 1 : struct spdk_fio_oat_ctx *ctx = arg;
995 : 1 : struct fio_file *f = ctx->u.zma.f;
996 : 1 : enum zbd_zoned_model *model = ctx->u.zma.model;
997 : : struct spdk_bdev *bdev;
998 : :
999 [ - + ]: 1 : if (f->filetype != FIO_TYPE_BLOCK) {
1000 : 0 : SPDK_ERRLOG("Unsupported filetype: %d\n", f->filetype);
1001 : 0 : ctx->ret = -EINVAL;
1002 : 0 : goto out;
1003 : : }
1004 : :
1005 : 1 : bdev = spdk_bdev_get_by_name(f->file_name);
1006 [ - + ]: 1 : if (!bdev) {
1007 : 0 : SPDK_ERRLOG("Cannot get zoned model, no bdev with name: %s\n", f->file_name);
1008 : 0 : ctx->ret = -ENODEV;
1009 : 0 : goto out;
1010 : : }
1011 : :
1012 [ + - ]: 1 : if (spdk_bdev_is_zoned(bdev)) {
1013 : 1 : *model = ZBD_HOST_MANAGED;
1014 : : } else {
1015 : 0 : *model = ZBD_NONE;
1016 : : }
1017 : :
1018 : 1 : ctx->ret = 0;
1019 : 1 : out:
1020 : 1 : spdk_fio_wake_oat_waiter(ctx);
1021 : 1 : }
1022 : :
1023 : : static int
1024 : 1 : spdk_fio_get_zoned_model(struct thread_data *td, struct fio_file *f, enum zbd_zoned_model *model)
1025 : : {
1026 : 1 : struct spdk_fio_oat_ctx ctx = { 0 };
1027 : :
1028 : 1 : ctx.u.zma.f = f;
1029 : 1 : ctx.u.zma.model = model;
1030 : :
1031 : 1 : spdk_fio_sync_run_oat(spdk_fio_get_zoned_model_oat, &ctx);
1032 : :
1033 : 1 : return ctx.ret;
1034 : : }
1035 : :
1036 : :
1037 : : static void
1038 : 1 : spdk_fio_bdev_get_zone_info_done(struct spdk_bdev_io *bdev_io, bool success, void *arg)
1039 : : {
1040 : 1 : struct spdk_fio_zone_cb_arg *cb_arg = arg;
1041 : : unsigned int i;
1042 : 1 : int handled_zones = 0;
1043 : :
1044 [ - + ]: 1 : if (!success) {
1045 : 0 : spdk_bdev_free_io(bdev_io);
1046 : 0 : cb_arg->completed = -EIO;
1047 : 0 : return;
1048 : : }
1049 : :
1050 [ + + ]: 41 : for (i = 0; i < cb_arg->nr_zones; i++) {
1051 : 40 : struct spdk_bdev_zone_info *zone_src = &cb_arg->spdk_zones[handled_zones];
1052 : 40 : struct zbd_zone *zone_dest = &cb_arg->fio_zones[handled_zones];
1053 : 40 : uint32_t block_size = spdk_bdev_get_block_size(cb_arg->target->bdev);
1054 : :
1055 [ + - - - ]: 40 : switch (zone_src->type) {
1056 : 40 : case SPDK_BDEV_ZONE_TYPE_SEQWR:
1057 : 40 : zone_dest->type = ZBD_ZONE_TYPE_SWR;
1058 : 40 : break;
1059 : 0 : case SPDK_BDEV_ZONE_TYPE_SEQWP:
1060 : 0 : zone_dest->type = ZBD_ZONE_TYPE_SWP;
1061 : 0 : break;
1062 : 0 : case SPDK_BDEV_ZONE_TYPE_CNV:
1063 : 0 : zone_dest->type = ZBD_ZONE_TYPE_CNV;
1064 : 0 : break;
1065 : 0 : default:
1066 : 0 : spdk_bdev_free_io(bdev_io);
1067 : 0 : cb_arg->completed = -EIO;
1068 : 0 : return;
1069 : : }
1070 : :
1071 : 40 : zone_dest->len = spdk_bdev_get_zone_size(cb_arg->target->bdev) * block_size;
1072 : 40 : zone_dest->capacity = zone_src->capacity * block_size;
1073 : 40 : zone_dest->start = zone_src->zone_id * block_size;
1074 : 40 : zone_dest->wp = zone_src->write_pointer * block_size;
1075 : :
1076 [ + - - - : 40 : switch (zone_src->state) {
- - - -
- ]
1077 : 40 : case SPDK_BDEV_ZONE_STATE_EMPTY:
1078 : 40 : zone_dest->cond = ZBD_ZONE_COND_EMPTY;
1079 : 40 : break;
1080 : 0 : case SPDK_BDEV_ZONE_STATE_IMP_OPEN:
1081 : 0 : zone_dest->cond = ZBD_ZONE_COND_IMP_OPEN;
1082 : 0 : break;
1083 : 0 : case SPDK_BDEV_ZONE_STATE_EXP_OPEN:
1084 : 0 : zone_dest->cond = ZBD_ZONE_COND_EXP_OPEN;
1085 : 0 : break;
1086 : 0 : case SPDK_BDEV_ZONE_STATE_FULL:
1087 : 0 : zone_dest->cond = ZBD_ZONE_COND_FULL;
1088 : 0 : break;
1089 : 0 : case SPDK_BDEV_ZONE_STATE_CLOSED:
1090 : 0 : zone_dest->cond = ZBD_ZONE_COND_CLOSED;
1091 : 0 : break;
1092 : 0 : case SPDK_BDEV_ZONE_STATE_READ_ONLY:
1093 : 0 : zone_dest->cond = ZBD_ZONE_COND_READONLY;
1094 : 0 : break;
1095 : 0 : case SPDK_BDEV_ZONE_STATE_OFFLINE:
1096 : 0 : zone_dest->cond = ZBD_ZONE_COND_OFFLINE;
1097 : 0 : break;
1098 : 0 : case SPDK_BDEV_ZONE_STATE_NOT_WP:
1099 : 0 : 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 : 0 : zone_dest->wp = zone_dest->start + zone_dest->capacity;
1102 : 0 : break;
1103 : 0 : default:
1104 : 0 : spdk_bdev_free_io(bdev_io);
1105 : 0 : cb_arg->completed = -EIO;
1106 : 0 : return;
1107 : : }
1108 : 40 : handled_zones++;
1109 : : }
1110 : :
1111 : 1 : spdk_bdev_free_io(bdev_io);
1112 : 1 : cb_arg->completed = handled_zones;
1113 : : }
1114 : :
1115 : : static void
1116 : 1 : spdk_fio_bdev_get_zone_info(void *arg)
1117 : : {
1118 : 1 : struct spdk_fio_zone_cb_arg *cb_arg = arg;
1119 : 1 : struct spdk_fio_target *target = cb_arg->target;
1120 : : int rc;
1121 : :
1122 : 1 : rc = spdk_bdev_get_zone_info(target->desc, target->ch, cb_arg->offset_blocks,
1123 : 1 : cb_arg->nr_zones, cb_arg->spdk_zones,
1124 : : spdk_fio_bdev_get_zone_info_done, cb_arg);
1125 [ - + ]: 1 : if (rc < 0) {
1126 : 0 : cb_arg->completed = rc;
1127 : : }
1128 : 1 : }
1129 : :
1130 : : static int
1131 : 1 : 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 : 0 : struct spdk_fio_zone_cb_arg cb_arg;
1137 : : uint32_t block_size;
1138 : : int rc;
1139 : :
1140 [ - + ]: 1 : if (nr_zones == 0) {
1141 : 0 : 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 : 1 : rc = spdk_fio_init(td);
1152 [ - + ]: 1 : if (rc) {
1153 : 0 : return rc;
1154 : : }
1155 : 1 : fio_thread = td->io_ops_data;
1156 : 1 : target = f->engine_data;
1157 : :
1158 [ - + ]: 1 : assert(fio_thread);
1159 [ - + ]: 1 : assert(target);
1160 : :
1161 : 1 : block_size = spdk_bdev_get_block_size(target->bdev);
1162 : :
1163 : 1 : cb_arg.target = target;
1164 : 1 : cb_arg.completed = 0;
1165 [ # # ]: 1 : cb_arg.offset_blocks = offset / block_size;
1166 : 1 : cb_arg.fio_zones = zones;
1167 [ + - ]: 1 : cb_arg.nr_zones = spdk_min(nr_zones, spdk_bdev_get_num_zones(target->bdev));
1168 : :
1169 : 1 : cb_arg.spdk_zones = calloc(1, sizeof(*cb_arg.spdk_zones) * cb_arg.nr_zones);
1170 [ - + ]: 1 : if (!cb_arg.spdk_zones) {
1171 : 0 : SPDK_ERRLOG("Could not allocate memory for zone report!\n");
1172 : 0 : rc = -ENOMEM;
1173 : 0 : goto cleanup_thread;
1174 : : }
1175 : :
1176 : 1 : spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_get_zone_info, &cb_arg);
1177 : : do {
1178 : 248 : spdk_fio_poll_thread(fio_thread);
1179 [ + + ]: 248 : } 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 : 1 : free(cb_arg.spdk_zones);
1183 : :
1184 : 1 : rc = cb_arg.completed;
1185 [ - + ]: 1 : if (rc < 0) {
1186 : 0 : SPDK_ERRLOG("Failed to get zone info: %d\n", rc);
1187 : 0 : goto cleanup_thread;
1188 : : }
1189 : :
1190 : : /* Return the amount of zones successfully copied. */
1191 : 1 : return rc;
1192 : :
1193 : 0 : cleanup_thread:
1194 : 0 : spdk_fio_cleanup(td);
1195 : :
1196 : 0 : return rc;
1197 : : }
1198 : :
1199 : : static void
1200 : 43 : spdk_fio_bdev_zone_reset_done(struct spdk_bdev_io *bdev_io, bool success, void *arg)
1201 : : {
1202 : 43 : struct spdk_fio_zone_cb_arg *cb_arg = arg;
1203 : :
1204 : 43 : spdk_bdev_free_io(bdev_io);
1205 : :
1206 [ - + ]: 43 : if (!success) {
1207 : 0 : cb_arg->completed = -EIO;
1208 : : } else {
1209 : 43 : cb_arg->completed = 1;
1210 : : }
1211 : 43 : }
1212 : :
1213 : : static void
1214 : 43 : spdk_fio_bdev_zone_reset(void *arg)
1215 : : {
1216 : 43 : struct spdk_fio_zone_cb_arg *cb_arg = arg;
1217 : 43 : struct spdk_fio_target *target = cb_arg->target;
1218 : : int rc;
1219 : :
1220 : 43 : 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 [ - + ]: 43 : if (rc < 0) {
1224 : 0 : cb_arg->completed = rc;
1225 : : }
1226 : 43 : }
1227 : :
1228 : : static int
1229 : 4 : 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 [ - + ]: 4 : assert(fio_thread);
1237 [ - + ]: 4 : assert(target);
1238 : :
1239 : 4 : block_size = spdk_bdev_get_block_size(target->bdev);
1240 : 4 : zone_size_bytes = spdk_bdev_get_zone_size(target->bdev) * block_size;
1241 : :
1242 [ + + ]: 47 : for (uint64_t cur = offset; cur < offset + length; cur += zone_size_bytes) {
1243 [ # # ]: 43 : struct spdk_fio_zone_cb_arg cb_arg = {
1244 : : .target = target,
1245 : : .completed = 0,
1246 : 43 : .offset_blocks = cur / block_size,
1247 : : };
1248 : :
1249 : 43 : spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_zone_reset, &cb_arg);
1250 : : do {
1251 : 394283 : spdk_fio_poll_thread(fio_thread);
1252 [ + + ]: 394283 : } while (!cb_arg.completed);
1253 : :
1254 : 43 : rc = cb_arg.completed;
1255 [ - + ]: 43 : if (rc < 0) {
1256 : 0 : SPDK_ERRLOG("Failed to reset zone: %d\n", rc);
1257 : 0 : return rc;
1258 : : }
1259 : : }
1260 : :
1261 : 4 : return 0;
1262 : : }
1263 : :
1264 : : static int
1265 : 3 : spdk_fio_reset_wp(struct thread_data *td, struct fio_file *f, uint64_t offset, uint64_t length)
1266 : : {
1267 : 3 : 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 : 1 : spdk_fio_get_max_open_zones_oat(void *_ctx)
1274 : : {
1275 : 1 : struct spdk_fio_oat_ctx *ctx = _ctx;
1276 : 1 : struct fio_file *f = ctx->u.moza.f;
1277 : : struct spdk_bdev *bdev;
1278 : :
1279 : 1 : bdev = spdk_bdev_get_by_name(f->file_name);
1280 [ - + ]: 1 : if (!bdev) {
1281 : 0 : SPDK_ERRLOG("Cannot get max open zones, no bdev with name: %s\n", f->file_name);
1282 : 0 : ctx->ret = -ENODEV;
1283 : : } else {
1284 : 1 : *ctx->u.moza.max_open_zones = spdk_bdev_get_max_open_zones(bdev);
1285 : 1 : ctx->ret = 0;
1286 : : }
1287 : :
1288 : 1 : spdk_fio_wake_oat_waiter(ctx);
1289 : 1 : }
1290 : :
1291 : : static int
1292 : 1 : spdk_fio_get_max_open_zones(struct thread_data *td, struct fio_file *f,
1293 : : unsigned int *max_open_zones)
1294 : : {
1295 : 1 : struct spdk_fio_oat_ctx ctx = { 0 };
1296 : :
1297 : 1 : ctx.u.moza.f = f;
1298 : 1 : ctx.u.moza.max_open_zones = max_open_zones;
1299 : :
1300 : 1 : spdk_fio_sync_run_oat(spdk_fio_get_max_open_zones_oat, &ctx);
1301 : :
1302 : 1 : return ctx.ret;
1303 : : }
1304 : : #endif
1305 : :
1306 : : static int
1307 : 309 : spdk_fio_handle_options(struct thread_data *td, struct fio_file *f, struct spdk_bdev *bdev)
1308 : : {
1309 : 309 : struct spdk_fio_options *fio_options = td->eo;
1310 : :
1311 [ + + + - ]: 309 : if (fio_options->initial_zone_reset && spdk_bdev_is_zoned(bdev)) {
1312 : : #if FIO_HAS_ZBD
1313 : 1 : int rc = spdk_fio_init(td);
1314 [ - + ]: 1 : if (rc) {
1315 : 0 : return rc;
1316 : : }
1317 : : /* offset used to indicate conventional zones that need to be skipped (reset not allowed) */
1318 : 1 : rc = spdk_fio_reset_zones(td->io_ops_data, f->engine_data, td->o.start_offset,
1319 : 1 : f->real_file_size - td->o.start_offset);
1320 [ - + ]: 1 : if (rc) {
1321 : 0 : spdk_fio_cleanup(td);
1322 : 0 : return rc;
1323 : : }
1324 : : #else
1325 : : SPDK_ERRLOG("fio version is too old to support zoned block devices\n");
1326 : : #endif
1327 : : }
1328 : :
1329 : 309 : return 0;
1330 : : }
1331 : :
1332 : : static int
1333 : 309 : spdk_fio_handle_options_per_target(struct thread_data *td, struct fio_file *f)
1334 : : {
1335 : 309 : struct spdk_fio_target *target = f->engine_data;
1336 : 309 : struct spdk_fio_options *fio_options = td->eo;
1337 : :
1338 [ + + + - ]: 309 : if (fio_options->zone_append && spdk_bdev_is_zoned(target->bdev)) {
1339 [ + - ]: 1 : if (spdk_bdev_io_type_supported(target->bdev, SPDK_BDEV_IO_TYPE_ZONE_APPEND)) {
1340 [ - + # # ]: 1 : SPDK_DEBUGLOG(fio_bdev, "Using zone appends instead of writes on: '%s'\n",
1341 : : f->file_name);
1342 : 1 : target->zone_append_enabled = true;
1343 : : } else {
1344 : 0 : SPDK_WARNLOG("Falling back to writes on: '%s' - bdev lacks zone append cmd\n",
1345 : : f->file_name);
1346 : : }
1347 : : }
1348 : :
1349 : 309 : 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 : 45 : spdk_fio_register(void)
1481 : : {
1482 : 45 : register_ioengine(&ioengine);
1483 : 45 : }
1484 : :
1485 : : static void
1486 : 45 : spdk_fio_finish_env(void)
1487 : : {
1488 [ - + ]: 45 : pthread_mutex_lock(&g_init_mtx);
1489 : 45 : g_poll_loop = false;
1490 [ - + ]: 45 : pthread_cond_signal(&g_init_cond);
1491 [ - + ]: 45 : pthread_mutex_unlock(&g_init_mtx);
1492 : 45 : pthread_join(g_init_thread_id, NULL);
1493 : :
1494 : 45 : spdk_thread_lib_fini();
1495 : 45 : spdk_env_fini();
1496 : 45 : }
1497 : :
1498 : : static void fio_exit
1499 : 45 : spdk_fio_unregister(void)
1500 : : {
1501 [ + + + - ]: 45 : if (g_spdk_env_initialized) {
1502 : 45 : spdk_fio_finish_env();
1503 : 45 : g_spdk_env_initialized = false;
1504 : : }
1505 : 45 : unregister_ioengine(&ioengine);
1506 : 45 : }
1507 : :
1508 : 45 : SPDK_LOG_REGISTER_COMPONENT(fio_bdev)
|