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