Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2016 Intel Corporation. All rights reserved.
3 : : * Copyright (c) 2019 Mellanox Technologies LTD. All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : :
8 : : #include "spdk/nvme.h"
9 : : #include "spdk/nvme_zns.h"
10 : : #include "spdk/vmd.h"
11 : : #include "spdk/env.h"
12 : : #include "spdk/string.h"
13 : : #include "spdk/log.h"
14 : : #include "spdk/likely.h"
15 : : #include "spdk/endian.h"
16 : : #include "spdk/dif.h"
17 : : #include "spdk/util.h"
18 : : #include "spdk/trace.h"
19 : :
20 : : #include "config-host.h"
21 : : #include "fio.h"
22 : : #include "optgroup.h"
23 : :
24 : : #ifdef for_each_rw_ddir
25 : : #define FIO_HAS_ZBD (FIO_IOOPS_VERSION >= 26)
26 : : #define FIO_HAS_FDP (FIO_IOOPS_VERSION >= 32)
27 : : #else
28 : : #define FIO_HAS_ZBD (0)
29 : : #define FIO_HAS_FDP (0)
30 : : #endif
31 : :
32 : : /* FreeBSD is missing CLOCK_MONOTONIC_RAW,
33 : : * so alternative is provided. */
34 : : #ifndef CLOCK_MONOTONIC_RAW /* Defined in glibc bits/time.h */
35 : : #define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
36 : : #endif
37 : :
38 : : #define NVME_IO_ALIGN 4096
39 : :
40 : : static bool g_spdk_env_initialized;
41 : : static bool g_log_flag_error;
42 : : static int g_spdk_enable_sgl = 0;
43 : : static uint32_t g_spdk_sge_size = 4096;
44 : : static uint32_t g_spdk_bit_bucket_data_len = 0;
45 : : static uint32_t g_spdk_pract_flag;
46 : : static uint32_t g_spdk_prchk_flags;
47 : : static uint32_t g_spdk_md_per_io_size = 4096;
48 : : static uint16_t g_spdk_apptag;
49 : : static uint16_t g_spdk_apptag_mask;
50 : :
51 : : struct spdk_fio_options {
52 : : void *pad; /* off1 used in option descriptions may not be 0 */
53 : : int enable_wrr;
54 : : int arbitration_burst;
55 : : int low_weight;
56 : : int medium_weight;
57 : : int high_weight;
58 : : int wrr_priority;
59 : : int mem_size;
60 : : int shm_id;
61 : : int enable_sgl;
62 : : int sge_size;
63 : : int bit_bucket_data_len;
64 : : char *hostnqn;
65 : : int pi_act;
66 : : char *pi_chk;
67 : : int md_per_io_size;
68 : : int apptag;
69 : : int apptag_mask;
70 : : char *digest_enable;
71 : : int enable_vmd;
72 : : int initial_zone_reset;
73 : : int zone_append;
74 : : int print_qid_mappings;
75 : : int spdk_tracing;
76 : : char *log_flags;
77 : : };
78 : :
79 : : struct spdk_fio_request {
80 : : struct io_u *io;
81 : : /** Offset in current iovec, fio only uses 1 vector */
82 : : uint32_t iov_offset;
83 : :
84 : : /** Amount of data used for Bit Bucket SGL */
85 : : uint32_t bit_bucket_data_len;
86 : :
87 : : /** Context for NVMe PI */
88 : : struct spdk_dif_ctx dif_ctx;
89 : : /** Separate metadata buffer pointer */
90 : : void *md_buf;
91 : :
92 : : struct spdk_fio_thread *fio_thread;
93 : : struct spdk_fio_qpair *fio_qpair;
94 : : };
95 : :
96 : : struct spdk_fio_ctrlr {
97 : : struct spdk_nvme_transport_id tr_id;
98 : : struct spdk_nvme_ctrlr_opts opts;
99 : : struct spdk_nvme_ctrlr *ctrlr;
100 : : TAILQ_ENTRY(spdk_fio_ctrlr) link;
101 : : };
102 : :
103 : : static TAILQ_HEAD(, spdk_fio_ctrlr) g_ctrlrs = TAILQ_HEAD_INITIALIZER(g_ctrlrs);
104 : : static int g_td_count;
105 : : static pthread_t g_ctrlr_thread_id = 0;
106 : : static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
107 : : static bool g_error;
108 : :
109 : : struct spdk_fio_qpair {
110 : : struct fio_file *f;
111 : : struct spdk_nvme_qpair *qpair;
112 : : struct spdk_nvme_ns *ns;
113 : : uint32_t io_flags;
114 : : bool zone_append_enabled;
115 : : bool nvme_pi_enabled;
116 : : /* True for DIF and false for DIX, and this is valid only if nvme_pi_enabled is true. */
117 : : bool extended_lba;
118 : : /* True for protection info transferred at start of metadata,
119 : : * false for protection info transferred at end of metadata, and
120 : : * this is valid only if nvme_pi_enabled is true.
121 : : */
122 : : bool md_start;
123 : : TAILQ_ENTRY(spdk_fio_qpair) link;
124 : : struct spdk_fio_ctrlr *fio_ctrlr;
125 : : };
126 : :
127 : : struct spdk_fio_thread {
128 : : struct thread_data *td;
129 : :
130 : : TAILQ_HEAD(, spdk_fio_qpair) fio_qpair;
131 : : struct spdk_fio_qpair *fio_qpair_current; /* the current fio_qpair to be handled. */
132 : :
133 : : struct io_u **iocq; /* io completion queue */
134 : : unsigned int iocq_count; /* number of iocq entries filled by last getevents */
135 : : unsigned int iocq_size; /* number of iocq entries allocated */
136 : : struct fio_file *current_f; /* fio_file given by user */
137 : :
138 : : };
139 : :
140 : : static void *
141 : 30 : spdk_fio_poll_ctrlrs(void *arg)
142 : : {
143 : : struct spdk_fio_ctrlr *fio_ctrlr;
144 : 6 : int oldstate;
145 : : int rc;
146 : :
147 : : /* Loop until the thread is cancelled */
148 : : while (true) {
149 : 99 : rc = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
150 [ - + ]: 99 : if (rc != 0) {
151 : 0 : SPDK_ERRLOG("Unable to set cancel state disabled on g_init_thread (%d): %s\n",
152 : : rc, spdk_strerror(rc));
153 : : }
154 : :
155 [ - + ]: 99 : pthread_mutex_lock(&g_mutex);
156 : :
157 [ + + ]: 159 : TAILQ_FOREACH(fio_ctrlr, &g_ctrlrs, link) {
158 : 60 : spdk_nvme_ctrlr_process_admin_completions(fio_ctrlr->ctrlr);
159 : : }
160 : :
161 [ - + ]: 99 : pthread_mutex_unlock(&g_mutex);
162 : :
163 : 99 : rc = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
164 [ - + ]: 99 : if (rc != 0) {
165 : 0 : SPDK_ERRLOG("Unable to set cancel state enabled on g_init_thread (%d): %s\n",
166 : : rc, spdk_strerror(rc));
167 : : }
168 : :
169 : : /* This is a pthread cancellation point and cannot be removed. */
170 : 99 : sleep(1);
171 : : }
172 : :
173 : : return NULL;
174 : : }
175 : :
176 : : static bool
177 : 30 : probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
178 : : struct spdk_nvme_ctrlr_opts *opts)
179 : : {
180 : 30 : struct thread_data *td = cb_ctx;
181 : 30 : struct spdk_fio_options *fio_options = td->eo;
182 : :
183 [ - + ]: 30 : if (fio_options->hostnqn) {
184 : 0 : snprintf(opts->hostnqn, sizeof(opts->hostnqn), "%s", fio_options->hostnqn);
185 : : }
186 : :
187 [ - + ]: 30 : if (fio_options->enable_wrr) {
188 : 0 : opts->arb_mechanism = SPDK_NVME_CC_AMS_WRR;
189 : 0 : opts->arbitration_burst = fio_options->arbitration_burst;
190 : 0 : opts->low_priority_weight = fio_options->low_weight;
191 : 0 : opts->medium_priority_weight = fio_options->medium_weight;
192 : 0 : opts->high_priority_weight = fio_options->high_weight;
193 : : }
194 : :
195 [ - + ]: 30 : if (fio_options->digest_enable) {
196 [ # # # # ]: 0 : if (strcasecmp(fio_options->digest_enable, "HEADER") == 0) {
197 : 0 : opts->header_digest = true;
198 [ # # # # ]: 0 : } else if (strcasecmp(fio_options->digest_enable, "DATA") == 0) {
199 : 0 : opts->data_digest = true;
200 [ # # # # ]: 0 : } else if (strcasecmp(fio_options->digest_enable, "BOTH") == 0) {
201 : 0 : opts->header_digest = true;
202 : 0 : opts->data_digest = true;
203 : : }
204 : : }
205 : :
206 : 30 : return true;
207 : : }
208 : :
209 : : static struct spdk_fio_ctrlr *
210 : 60 : get_fio_ctrlr(const struct spdk_nvme_transport_id *trid)
211 : : {
212 : : struct spdk_fio_ctrlr *fio_ctrlr;
213 : :
214 [ - + ]: 60 : TAILQ_FOREACH(fio_ctrlr, &g_ctrlrs, link) {
215 [ # # ]: 0 : if (spdk_nvme_transport_id_compare(trid, &fio_ctrlr->tr_id) == 0) {
216 : 0 : return fio_ctrlr;
217 : : }
218 : : }
219 : :
220 : 60 : return NULL;
221 : : }
222 : :
223 : : /**
224 : : * Returns the fio_qpair matching the given fio_file and has an associated ns
225 : : */
226 : : static struct spdk_fio_qpair *
227 : 1811866 : get_fio_qpair(struct spdk_fio_thread *fio_thread, struct fio_file *f)
228 : : {
229 : : struct spdk_fio_qpair *fio_qpair;
230 : :
231 [ + - ]: 1811866 : TAILQ_FOREACH(fio_qpair, &fio_thread->fio_qpair, link) {
232 [ + - + - ]: 1811866 : if ((fio_qpair->f == f) && fio_qpair->ns) {
233 : 1811866 : return fio_qpair;
234 : : }
235 : : }
236 : :
237 : 0 : return NULL;
238 : : }
239 : :
240 : : #if FIO_HAS_ZBD
241 : : /**
242 : : * Callback function to use while processing completions until completion-indicator turns non-zero
243 : : */
244 : : static void
245 : 0 : pcu_cb(void *ctx, const struct spdk_nvme_cpl *cpl)
246 : : {
247 : 0 : int *completed = ctx;
248 : :
249 [ # # # # ]: 0 : *completed = spdk_nvme_cpl_is_error(cpl) ? -1 : 1;
250 : 0 : }
251 : :
252 : : /**
253 : : * Process Completions Until the given 'completed' indicator turns non-zero or an error occurs
254 : : */
255 : : static int32_t
256 : 0 : pcu(struct spdk_nvme_qpair *qpair, int *completed)
257 : : {
258 : : int32_t ret;
259 : :
260 [ # # ]: 0 : while (!*completed) {
261 : 0 : ret = spdk_nvme_qpair_process_completions(qpair, 1);
262 [ # # ]: 0 : if (ret < 0) {
263 : 0 : log_err("spdk/nvme: process_compl(): ret: %d\n", ret);
264 : 0 : return ret;
265 : : }
266 : : }
267 : :
268 : 0 : return 0;
269 : : }
270 : : #endif
271 : :
272 : : static inline uint32_t
273 : 1811896 : _nvme_get_host_buffer_sector_size(struct spdk_nvme_ns *ns, uint32_t io_flags)
274 : : {
275 : 1811896 : bool md_excluded_from_xfer = false;
276 : : uint32_t md_size;
277 : : uint32_t ns_flags;
278 : :
279 : 1811896 : ns_flags = spdk_nvme_ns_get_flags(ns);
280 : 1811896 : md_size = spdk_nvme_ns_get_md_size(ns);
281 : :
282 : : /* For extended LBA format, if the metadata size is 8 bytes and PRACT is
283 : : * enabled(controller inserts/strips PI), we should reduce metadata size
284 : : * from block size.
285 : : */
286 : 3623792 : md_excluded_from_xfer = ((io_flags & SPDK_NVME_IO_FLAGS_PRACT) &&
287 [ # # ]: 0 : (ns_flags & SPDK_NVME_NS_EXTENDED_LBA_SUPPORTED) &&
288 [ - + - - : 1811896 : (ns_flags & SPDK_NVME_NS_DPS_PI_SUPPORTED) &&
- - ]
289 : : (md_size == 8));
290 : :
291 [ - + ]: 3623792 : return md_excluded_from_xfer ? spdk_nvme_ns_get_sector_size(ns) :
292 : 1811896 : spdk_nvme_ns_get_extended_sector_size(ns);
293 : : }
294 : :
295 : : static void
296 : 30 : attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
297 : : struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
298 : : {
299 : 30 : struct thread_data *td = cb_ctx;
300 : 30 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
301 : : struct spdk_fio_ctrlr *fio_ctrlr;
302 : : struct spdk_fio_qpair *fio_qpair;
303 : : struct spdk_nvme_ns *ns;
304 : : const struct spdk_nvme_ns_data *nsdata;
305 : 30 : struct fio_file *f = fio_thread->current_f;
306 : : uint32_t ns_id;
307 : : char *p;
308 : : long int tmp;
309 : : uint32_t block_size;
310 : 30 : struct spdk_fio_options *fio_options = td->eo;
311 : :
312 [ - + ]: 30 : p = strstr(f->file_name, "ns=");
313 [ + + ]: 30 : if (p != NULL) {
314 : 20 : tmp = spdk_strtol(p + 3, 10);
315 [ - + ]: 20 : if (tmp <= 0) {
316 : 0 : SPDK_ERRLOG("namespace id should be >=1, but was invalid: %ld\n", tmp);
317 : 0 : g_error = true;
318 : 0 : return;
319 : : }
320 : 20 : ns_id = (uint32_t)tmp;
321 : : } else {
322 : 10 : ns_id = spdk_nvme_ctrlr_get_first_active_ns(ctrlr);
323 [ - + ]: 10 : if (ns_id == 0) {
324 : : /* The ctrlr has no active namespaces and we didn't specify any so nothing to do. */
325 : 0 : return;
326 : : }
327 : : }
328 : :
329 : 30 : pthread_mutex_lock(&g_mutex);
330 : 30 : fio_ctrlr = get_fio_ctrlr(trid);
331 : : /* it is a new ctrlr and needs to be added */
332 [ + - ]: 30 : if (!fio_ctrlr) {
333 : : /* Create an fio_ctrlr and add it to the list */
334 : 30 : fio_ctrlr = calloc(1, sizeof(*fio_ctrlr));
335 [ - + ]: 30 : if (!fio_ctrlr) {
336 : 0 : SPDK_ERRLOG("Cannot allocate space for fio_ctrlr\n");
337 : 0 : g_error = true;
338 : 0 : pthread_mutex_unlock(&g_mutex);
339 : 0 : return;
340 : : }
341 : 30 : fio_ctrlr->opts = *opts;
342 : 30 : fio_ctrlr->ctrlr = ctrlr;
343 : 30 : fio_ctrlr->tr_id = *trid;
344 : 30 : TAILQ_INSERT_TAIL(&g_ctrlrs, fio_ctrlr, link);
345 : : }
346 : 30 : pthread_mutex_unlock(&g_mutex);
347 : :
348 : 30 : ns = spdk_nvme_ctrlr_get_ns(fio_ctrlr->ctrlr, ns_id);
349 [ - + ]: 30 : if (ns == NULL) {
350 : 0 : SPDK_ERRLOG("Cannot get namespace by ns_id=%d\n", ns_id);
351 : 0 : g_error = true;
352 : 0 : return;
353 : : }
354 : :
355 [ - + ]: 30 : if (!spdk_nvme_ns_is_active(ns)) {
356 : 0 : SPDK_ERRLOG("Inactive namespace by ns_id=%d\n", ns_id);
357 : 0 : g_error = true;
358 : 0 : return;
359 : : }
360 : 30 : nsdata = spdk_nvme_ns_get_data(ns);
361 : :
362 [ - + ]: 30 : TAILQ_FOREACH(fio_qpair, &fio_thread->fio_qpair, link) {
363 [ # # # # ]: 0 : if ((fio_qpair->f == f) ||
364 [ # # ]: 0 : ((spdk_nvme_transport_id_compare(trid, &fio_qpair->fio_ctrlr->tr_id) == 0) &&
365 : 0 : (spdk_nvme_ns_get_id(fio_qpair->ns) == ns_id))) {
366 : : /* Not the error case. Avoid duplicated connection */
367 : 0 : return;
368 : : }
369 : : }
370 : :
371 : : /* create a new qpair */
372 : 30 : fio_qpair = calloc(1, sizeof(*fio_qpair));
373 [ - + ]: 30 : if (!fio_qpair) {
374 : 0 : g_error = true;
375 : 0 : SPDK_ERRLOG("Cannot allocate space for fio_qpair\n");
376 : 0 : return;
377 : : }
378 : :
379 : 30 : f->engine_data = fio_qpair;
380 : 30 : fio_qpair->ns = ns;
381 : 30 : fio_qpair->f = f;
382 : 30 : fio_qpair->fio_ctrlr = fio_ctrlr;
383 : 30 : TAILQ_INSERT_TAIL(&fio_thread->fio_qpair, fio_qpair, link);
384 : :
385 [ - + ]: 30 : if (spdk_nvme_ns_get_flags(ns) & SPDK_NVME_NS_DPS_PI_SUPPORTED) {
386 [ # # ]: 0 : assert(spdk_nvme_ns_get_pi_type(ns) != SPDK_NVME_FMT_NVM_PROTECTION_DISABLE);
387 : 0 : fio_qpair->io_flags = g_spdk_pract_flag | g_spdk_prchk_flags;
388 : 0 : fio_qpair->nvme_pi_enabled = true;
389 : 0 : fio_qpair->md_start = nsdata->dps.md_start;
390 : 0 : fio_qpair->extended_lba = spdk_nvme_ns_supports_extended_lba(ns);
391 [ # # ]: 0 : fprintf(stdout, "PI type%u enabled with %s\n", spdk_nvme_ns_get_pi_type(ns),
392 [ # # # # ]: 0 : fio_qpair->extended_lba ? "extended lba" : "separate metadata");
393 : : }
394 : :
395 : 30 : block_size = _nvme_get_host_buffer_sector_size(ns, fio_qpair->io_flags);
396 [ + + ]: 120 : for_each_rw_ddir(ddir) {
397 [ + + + + : 90 : if (td->o.min_bs[ddir] % block_size != 0 || td->o.max_bs[ddir] % block_size != 0) {
- + - + ]
398 [ # # ]: 0 : if (spdk_nvme_ns_supports_extended_lba(ns)) {
399 : 0 : SPDK_ERRLOG("--bs or other block size related option has to be a multiple of (LBA data size + Metadata size)\n");
400 : : } else {
401 : 0 : SPDK_ERRLOG("--bs or other block size related option has to be a multiple of LBA data size\n");
402 : : }
403 : 0 : g_error = true;
404 : 0 : return;
405 : : }
406 : : }
407 : :
408 [ - + - - ]: 30 : if (fio_options->zone_append && spdk_nvme_ns_get_csi(ns) == SPDK_NVME_CSI_ZNS) {
409 [ # # ]: 0 : if (spdk_nvme_ctrlr_get_flags(ctrlr) & SPDK_NVME_CTRLR_ZONE_APPEND_SUPPORTED) {
410 [ # # # # ]: 0 : SPDK_DEBUGLOG(fio_nvme, "Using zone appends instead of writes on: '%s'\n",
411 : : f->file_name);
412 : 0 : fio_qpair->zone_append_enabled = true;
413 : : } else {
414 : 0 : SPDK_WARNLOG("Falling back to writes on: '%s' - ns lacks zone append cmd\n",
415 : : f->file_name);
416 : : }
417 : : }
418 : :
419 [ - + - - ]: 30 : if (fio_options->initial_zone_reset == 1 && spdk_nvme_ns_get_csi(ns) == SPDK_NVME_CSI_ZNS) {
420 : : #if FIO_HAS_ZBD
421 : : struct spdk_nvme_qpair *tmp_qpair;
422 : 0 : int completed = 0, err;
423 : :
424 : : /* qpair has not been allocated yet (it gets allocated in spdk_fio_open()).
425 : : * Create a temporary qpair in order to perform the initial zone reset.
426 : : */
427 [ # # ]: 0 : assert(!fio_qpair->qpair);
428 : :
429 : 0 : tmp_qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, NULL, 0);
430 [ # # ]: 0 : if (!tmp_qpair) {
431 : 0 : SPDK_ERRLOG("Cannot allocate a temporary qpair\n");
432 : 0 : g_error = true;
433 : 0 : return;
434 : : }
435 : :
436 : 0 : err = spdk_nvme_zns_reset_zone(ns, tmp_qpair, 0x0, true, pcu_cb, &completed);
437 [ # # # # : 0 : if (err || pcu(tmp_qpair, &completed) || completed < 0) {
# # ]
438 : 0 : log_err("spdk/nvme: warn: initial_zone_reset: err: %d, cpl: %d\n",
439 : : err, completed);
440 : : }
441 : :
442 : 0 : spdk_nvme_ctrlr_free_io_qpair(tmp_qpair);
443 : : #else
444 : : log_err("spdk/nvme: ZBD/ZNS is not supported\n");
445 : : #endif
446 : : }
447 : :
448 : 30 : f->real_file_size = spdk_nvme_ns_get_size(fio_qpair->ns);
449 [ - + ]: 30 : if (f->real_file_size <= 0) {
450 : 0 : g_error = true;
451 : 0 : SPDK_ERRLOG("Cannot get namespace size by ns=%p\n", ns);
452 : 0 : return;
453 : : }
454 : :
455 : 30 : f->filetype = FIO_TYPE_BLOCK;
456 : 30 : fio_file_set_size_known(f);
457 : : }
458 : :
459 : : static void
460 : 30 : parse_prchk_flags(const char *prchk_str)
461 : : {
462 [ + - ]: 30 : if (!prchk_str) {
463 : 30 : return;
464 : : }
465 : :
466 [ # # # # ]: 0 : if (strstr(prchk_str, "GUARD") != NULL) {
467 : 0 : g_spdk_prchk_flags = SPDK_NVME_IO_FLAGS_PRCHK_GUARD;
468 : : }
469 [ # # # # ]: 0 : if (strstr(prchk_str, "REFTAG") != NULL) {
470 : 0 : g_spdk_prchk_flags |= SPDK_NVME_IO_FLAGS_PRCHK_REFTAG;
471 : : }
472 [ # # # # ]: 0 : if (strstr(prchk_str, "APPTAG") != NULL) {
473 : 0 : g_spdk_prchk_flags |= SPDK_NVME_IO_FLAGS_PRCHK_APPTAG;
474 : : }
475 : : }
476 : :
477 : : static void
478 : 30 : parse_pract_flag(int pract)
479 : : {
480 [ + - ]: 30 : if (pract == 1) {
481 : 30 : g_spdk_pract_flag = SPDK_NVME_IO_FLAGS_PRACT;
482 : : } else {
483 : 0 : g_spdk_pract_flag = 0;
484 : : }
485 : 30 : }
486 : :
487 : : static bool
488 : 0 : fio_redirected_to_dev_null(void)
489 : : {
490 : 0 : char path[PATH_MAX] = "";
491 : : ssize_t ret;
492 : :
493 : 0 : ret = readlink("/proc/self/fd/1", path, sizeof(path));
494 : :
495 [ # # # # ]: 0 : if (ret == -1 || strcmp(path, "/dev/null") != 0) {
496 : 0 : return false;
497 : : }
498 : :
499 : 0 : ret = readlink("/proc/self/fd/2", path, sizeof(path));
500 : :
501 [ # # # # ]: 0 : if (ret == -1 || strcmp(path, "/dev/null") != 0) {
502 : 0 : return false;
503 : : }
504 : :
505 : 0 : return true;
506 : : }
507 : :
508 : : static int
509 : 30 : spdk_fio_init(struct thread_data *td)
510 : : {
511 : 30 : int ret = 0;
512 : 30 : struct spdk_fio_options *fio_options = td->eo;
513 : :
514 [ - + ]: 30 : if (fio_options->spdk_tracing) {
515 : 0 : ret = spdk_trace_register_user_thread();
516 : : }
517 : :
518 : 30 : return ret;
519 : : }
520 : :
521 : : /* Called once at initialization. This is responsible for gathering the size of
522 : : * each "file", which in our case are in the form
523 : : * 'key=value [key=value] ... ns=value'
524 : : * For example, For local PCIe NVMe device - 'trtype=PCIe traddr=0000.04.00.0 ns=1'
525 : : * For remote exported by NVMe-oF target, 'trtype=RDMA adrfam=IPv4 traddr=192.168.100.8 trsvcid=4420 ns=1' */
526 : : static int
527 : 30 : spdk_fio_setup(struct thread_data *td)
528 : : {
529 : : struct spdk_fio_thread *fio_thread;
530 : 30 : struct spdk_fio_options *fio_options = td->eo;
531 : 6 : struct spdk_env_opts opts;
532 : : struct fio_file *f;
533 : : char *p;
534 : 30 : int rc = 0;
535 : 6 : struct spdk_nvme_transport_id trid;
536 : : struct spdk_fio_ctrlr *fio_ctrlr;
537 : : char *trid_info;
538 : : unsigned int i;
539 : :
540 : : /*
541 : : * If we're running in a daemonized FIO instance, it's possible
542 : : * fd 1/2 were re-used for something important by FIO. Newer fio
543 : : * versions are careful to redirect those to /dev/null, but if we're
544 : : * not, we'll abort early, so we don't accidentally write messages to
545 : : * an important file, etc.
546 : : */
547 [ - + - + : 30 : if (is_backend && !fio_redirected_to_dev_null()) {
- - ]
548 : 0 : char buf[1024];
549 : 0 : snprintf(buf, sizeof(buf),
550 : : "SPDK FIO plugin is in daemon mode, but stdout/stderr "
551 : : "aren't redirected to /dev/null. Aborting.");
552 : 0 : fio_server_text_output(FIO_LOG_ERR, buf, sizeof(buf));
553 : 0 : return -1;
554 : : }
555 : :
556 [ - + ]: 30 : if (!td->o.use_thread) {
557 : 0 : log_err("spdk: must set thread=1 when using spdk plugin\n");
558 : 0 : return 1;
559 : : }
560 : :
561 [ - + - + ]: 30 : if (g_log_flag_error) {
562 : : /* The first thread found an error when parsing log flags, so
563 : : * just return error immediately for all of the other threads.
564 : : */
565 : 0 : return 1;
566 : : }
567 : :
568 : 30 : pthread_mutex_lock(&g_mutex);
569 : :
570 : 30 : fio_thread = calloc(1, sizeof(*fio_thread));
571 [ - + ]: 30 : assert(fio_thread != NULL);
572 : :
573 : 30 : td->io_ops_data = fio_thread;
574 : 30 : fio_thread->td = td;
575 : :
576 : 30 : fio_thread->iocq_size = td->o.iodepth;
577 : 30 : fio_thread->iocq = calloc(fio_thread->iocq_size, sizeof(struct io_u *));
578 [ - + ]: 30 : assert(fio_thread->iocq != NULL);
579 : :
580 : 30 : TAILQ_INIT(&fio_thread->fio_qpair);
581 : :
582 [ + + + - ]: 30 : if (!g_spdk_env_initialized) {
583 : 30 : spdk_env_opts_init(&opts);
584 : 30 : opts.name = "fio";
585 : 30 : opts.mem_size = fio_options->mem_size;
586 : 30 : opts.shm_id = fio_options->shm_id;
587 : 30 : g_spdk_enable_sgl = fio_options->enable_sgl;
588 : 30 : g_spdk_sge_size = fio_options->sge_size;
589 : 30 : g_spdk_bit_bucket_data_len = fio_options->bit_bucket_data_len;
590 : 30 : parse_pract_flag(fio_options->pi_act);
591 : 30 : g_spdk_md_per_io_size = spdk_max(fio_options->md_per_io_size, 4096);
592 : 30 : g_spdk_apptag = (uint16_t)fio_options->apptag;
593 : 30 : g_spdk_apptag_mask = (uint16_t)fio_options->apptag_mask;
594 : 30 : parse_prchk_flags(fio_options->pi_chk);
595 [ - + ]: 30 : if (spdk_env_init(&opts) < 0) {
596 : 0 : SPDK_ERRLOG("Unable to initialize SPDK env\n");
597 : 0 : free(fio_thread->iocq);
598 : 0 : free(fio_thread);
599 : 0 : fio_thread = NULL;
600 : 0 : pthread_mutex_unlock(&g_mutex);
601 : 0 : return 1;
602 : : }
603 : :
604 [ - + ]: 30 : if (fio_options->log_flags) {
605 : 0 : char *tok = strtok(fio_options->log_flags, ",");
606 : : do {
607 : 0 : rc = spdk_log_set_flag(tok);
608 [ # # ]: 0 : if (rc < 0) {
609 : 0 : SPDK_ERRLOG("unknown log flag %s\n", tok);
610 : 0 : g_log_flag_error = true;
611 : 0 : return 1;
612 : : }
613 [ # # ]: 0 : } while ((tok = strtok(NULL, ",")) != NULL);
614 : : #ifdef DEBUG
615 : 0 : spdk_log_set_print_level(SPDK_LOG_DEBUG);
616 : : #endif
617 : : }
618 : :
619 : 30 : g_spdk_env_initialized = true;
620 : 30 : spdk_unaffinitize_thread();
621 : :
622 [ - + ]: 30 : if (fio_options->spdk_tracing) {
623 : 0 : spdk_trace_init("spdk_fio_tracepoints", 65536, td->o.numjobs);
624 : 0 : spdk_trace_enable_tpoint_group("nvme_pcie");
625 : 0 : spdk_trace_enable_tpoint_group("nvme_tcp");
626 : : }
627 : :
628 : : /* Spawn a thread to continue polling the controllers */
629 : 30 : rc = pthread_create(&g_ctrlr_thread_id, NULL, &spdk_fio_poll_ctrlrs, NULL);
630 [ - + ]: 30 : if (rc != 0) {
631 : 0 : SPDK_ERRLOG("Unable to spawn a thread to poll admin queues. They won't be polled.\n");
632 : : }
633 : :
634 [ - + - - ]: 30 : if (fio_options->enable_vmd && spdk_vmd_init()) {
635 : 0 : SPDK_ERRLOG("Failed to initialize VMD. Some NVMe devices can be unavailable.\n");
636 : : }
637 : : }
638 : 30 : pthread_mutex_unlock(&g_mutex);
639 : :
640 [ + - + + : 60 : for_each_file(td, f, i) {
+ - ]
641 : 30 : memset(&trid, 0, sizeof(trid));
642 : :
643 : 30 : trid.trtype = SPDK_NVME_TRANSPORT_PCIE;
644 : :
645 [ - + ]: 30 : p = strstr(f->file_name, " ns=");
646 [ + + ]: 30 : if (p != NULL) {
647 [ - + ]: 20 : trid_info = strndup(f->file_name, p - f->file_name);
648 : : } else {
649 [ - + - + ]: 10 : trid_info = strndup(f->file_name, strlen(f->file_name));
650 : : }
651 : :
652 [ - + ]: 30 : if (!trid_info) {
653 : 0 : SPDK_ERRLOG("Failed to allocate space for trid_info\n");
654 : 0 : continue;
655 : : }
656 : :
657 : 30 : rc = spdk_nvme_transport_id_parse(&trid, trid_info);
658 [ - + ]: 30 : if (rc < 0) {
659 : 0 : SPDK_ERRLOG("Failed to parse given str: %s\n", trid_info);
660 : 0 : free(trid_info);
661 : 0 : continue;
662 : : }
663 : 30 : free(trid_info);
664 : :
665 [ + + ]: 30 : if (trid.trtype == SPDK_NVME_TRANSPORT_PCIE) {
666 : 6 : struct spdk_pci_addr pci_addr;
667 [ - + ]: 10 : if (spdk_pci_addr_parse(&pci_addr, trid.traddr) < 0) {
668 : 0 : SPDK_ERRLOG("Invalid traddr=%s\n", trid.traddr);
669 : 0 : continue;
670 : : }
671 : 10 : spdk_pci_addr_fmt(trid.traddr, sizeof(trid.traddr), &pci_addr);
672 : : } else {
673 [ + - ]: 20 : if (trid.subnqn[0] == '\0') {
674 : 20 : snprintf(trid.subnqn, sizeof(trid.subnqn), "%s",
675 : : SPDK_NVMF_DISCOVERY_NQN);
676 : : }
677 : : }
678 : :
679 : 30 : fio_thread->current_f = f;
680 : :
681 : 30 : pthread_mutex_lock(&g_mutex);
682 : 30 : fio_ctrlr = get_fio_ctrlr(&trid);
683 : 30 : pthread_mutex_unlock(&g_mutex);
684 [ - + ]: 30 : if (fio_ctrlr) {
685 : 0 : attach_cb(td, &trid, fio_ctrlr->ctrlr, &fio_ctrlr->opts);
686 : : } else {
687 : : /* Enumerate all of the controllers */
688 [ - + ]: 30 : if (spdk_nvme_probe(&trid, td, probe_cb, attach_cb, NULL) != 0) {
689 : 0 : SPDK_ERRLOG("spdk_nvme_probe() failed\n");
690 : 0 : continue;
691 : : }
692 : : }
693 : :
694 [ - + - + ]: 30 : if (g_error) {
695 : 0 : log_err("Failed to initialize spdk fio plugin\n");
696 : 0 : rc = 1;
697 : 0 : break;
698 : : }
699 : : }
700 : :
701 : 30 : pthread_mutex_lock(&g_mutex);
702 : 30 : g_td_count++;
703 : 30 : pthread_mutex_unlock(&g_mutex);
704 : :
705 : 30 : return rc;
706 : : }
707 : :
708 : : static int
709 : 64 : spdk_fio_open(struct thread_data *td, struct fio_file *f)
710 : : {
711 : 64 : struct spdk_fio_qpair *fio_qpair = f->engine_data;
712 : 64 : struct spdk_fio_ctrlr *fio_ctrlr = fio_qpair->fio_ctrlr;
713 : 64 : struct spdk_fio_options *fio_options = td->eo;
714 : 6 : struct spdk_nvme_io_qpair_opts qpopts;
715 : :
716 [ - + ]: 64 : assert(fio_qpair->qpair == NULL);
717 : 64 : spdk_nvme_ctrlr_get_default_io_qpair_opts(fio_ctrlr->ctrlr, &qpopts, sizeof(qpopts));
718 : 64 : qpopts.delay_cmd_submit = true;
719 [ - + ]: 64 : if (fio_options->enable_wrr) {
720 : 0 : qpopts.qprio = fio_options->wrr_priority;
721 : : }
722 : :
723 : 64 : fio_qpair->qpair = spdk_nvme_ctrlr_alloc_io_qpair(fio_ctrlr->ctrlr, &qpopts, sizeof(qpopts));
724 [ - + ]: 64 : if (!fio_qpair->qpair) {
725 : 0 : SPDK_ERRLOG("Cannot allocate nvme io_qpair any more\n");
726 : 0 : g_error = true;
727 : 0 : free(fio_qpair);
728 : 0 : return -1;
729 : : }
730 : :
731 [ - + ]: 64 : if (fio_options->print_qid_mappings == 1) {
732 : 0 : log_info("job %s: %s qid %d\n", td->o.name, f->file_name,
733 : 0 : spdk_nvme_qpair_get_id(fio_qpair->qpair));
734 : : }
735 : :
736 : 64 : return 0;
737 : : }
738 : :
739 : : static int
740 : 64 : spdk_fio_close(struct thread_data *td, struct fio_file *f)
741 : : {
742 : 64 : struct spdk_fio_qpair *fio_qpair = f->engine_data;
743 : :
744 [ - + ]: 64 : assert(fio_qpair->qpair != NULL);
745 : 64 : spdk_nvme_ctrlr_free_io_qpair(fio_qpair->qpair);
746 : 64 : fio_qpair->qpair = NULL;
747 : 64 : return 0;
748 : : }
749 : :
750 : : static int
751 : 30 : spdk_fio_iomem_alloc(struct thread_data *td, size_t total_mem)
752 : : {
753 : 30 : td->orig_buffer = spdk_dma_zmalloc(total_mem, NVME_IO_ALIGN, NULL);
754 : 30 : return td->orig_buffer == NULL;
755 : : }
756 : :
757 : : static void
758 : 30 : spdk_fio_iomem_free(struct thread_data *td)
759 : : {
760 : 30 : spdk_dma_free(td->orig_buffer);
761 : 30 : }
762 : :
763 : : static int
764 : 3840 : spdk_fio_io_u_init(struct thread_data *td, struct io_u *io_u)
765 : : {
766 : 3840 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
767 : : struct spdk_fio_request *fio_req;
768 : :
769 : 3840 : io_u->engine_data = NULL;
770 : :
771 : 3840 : fio_req = calloc(1, sizeof(*fio_req));
772 [ - + ]: 3840 : if (fio_req == NULL) {
773 : 0 : return 1;
774 : : }
775 : :
776 : 3840 : fio_req->md_buf = spdk_dma_zmalloc(g_spdk_md_per_io_size, NVME_IO_ALIGN, NULL);
777 [ - + ]: 3840 : if (fio_req->md_buf == NULL) {
778 [ # # # # ]: 0 : fprintf(stderr, "Allocate %u metadata failed\n", g_spdk_md_per_io_size);
779 : 0 : free(fio_req);
780 : 0 : return 1;
781 : : }
782 : :
783 : 3840 : fio_req->io = io_u;
784 : 3840 : fio_req->fio_thread = fio_thread;
785 : :
786 : 3840 : io_u->engine_data = fio_req;
787 : :
788 : 3840 : return 0;
789 : : }
790 : :
791 : : static void
792 : 3840 : spdk_fio_io_u_free(struct thread_data *td, struct io_u *io_u)
793 : : {
794 : 3840 : struct spdk_fio_request *fio_req = io_u->engine_data;
795 : :
796 [ + - ]: 3840 : if (fio_req) {
797 [ - + ]: 3840 : assert(fio_req->io == io_u);
798 : 3840 : spdk_dma_free(fio_req->md_buf);
799 : 3840 : free(fio_req);
800 : 3840 : io_u->engine_data = NULL;
801 : : }
802 : 3840 : }
803 : :
804 : : static inline uint64_t
805 : 0 : fio_offset_to_zslba(unsigned long long offset, struct spdk_nvme_ns *ns)
806 : : {
807 [ # # ]: 0 : return (offset / spdk_nvme_zns_ns_get_zone_size(ns)) * spdk_nvme_zns_ns_get_zone_size_sectors(ns);
808 : : }
809 : :
810 : : static int
811 : 0 : fio_extended_lba_setup_pi(struct spdk_fio_qpair *fio_qpair, struct io_u *io_u)
812 : : {
813 : 0 : struct spdk_nvme_ns *ns = fio_qpair->ns;
814 : 0 : struct spdk_fio_request *fio_req = io_u->engine_data;
815 : : uint32_t md_size, extended_lba_size, lba_count;
816 : : uint64_t lba;
817 : 0 : struct iovec iov;
818 : : int rc;
819 : 0 : struct spdk_dif_ctx_init_ext_opts dif_opts;
820 : :
821 : : /* Set appmask and apptag when PRACT is enabled */
822 [ # # ]: 0 : if (fio_qpair->io_flags & SPDK_NVME_IO_FLAGS_PRACT) {
823 : 0 : fio_req->dif_ctx.apptag_mask = g_spdk_apptag_mask;
824 : 0 : fio_req->dif_ctx.app_tag = g_spdk_apptag;
825 : 0 : return 0;
826 : : }
827 : :
828 : 0 : extended_lba_size = spdk_nvme_ns_get_extended_sector_size(ns);
829 : 0 : md_size = spdk_nvme_ns_get_md_size(ns);
830 [ # # ]: 0 : lba = io_u->offset / extended_lba_size;
831 [ # # ]: 0 : lba_count = io_u->xfer_buflen / extended_lba_size;
832 : :
833 : 0 : dif_opts.size = SPDK_SIZEOF(&dif_opts, dif_pi_format);
834 : 0 : dif_opts.dif_pi_format = SPDK_DIF_PI_FORMAT_16;
835 : 0 : rc = spdk_dif_ctx_init(&fio_req->dif_ctx, extended_lba_size, md_size,
836 [ # # ]: 0 : true, fio_qpair->md_start,
837 : 0 : (enum spdk_dif_type)spdk_nvme_ns_get_pi_type(ns),
838 : : fio_qpair->io_flags, lba, g_spdk_apptag_mask, g_spdk_apptag,
839 : : 0, 0, &dif_opts);
840 [ # # ]: 0 : if (rc != 0) {
841 [ # # # # ]: 0 : fprintf(stderr, "Initialization of DIF context failed\n");
842 : 0 : return rc;
843 : : }
844 : :
845 [ # # ]: 0 : if (io_u->ddir != DDIR_WRITE) {
846 : 0 : return 0;
847 : : }
848 : :
849 : 0 : iov.iov_base = io_u->buf;
850 : 0 : iov.iov_len = io_u->xfer_buflen;
851 : 0 : rc = spdk_dif_generate(&iov, 1, lba_count, &fio_req->dif_ctx);
852 [ # # ]: 0 : if (rc != 0) {
853 [ # # # # ]: 0 : fprintf(stderr, "Generation of DIF failed\n");
854 : : }
855 : :
856 : 0 : return rc;
857 : : }
858 : :
859 : : static int
860 : 0 : fio_separate_md_setup_pi(struct spdk_fio_qpair *fio_qpair, struct io_u *io_u)
861 : : {
862 : 0 : struct spdk_nvme_ns *ns = fio_qpair->ns;
863 : 0 : struct spdk_fio_request *fio_req = io_u->engine_data;
864 : : uint32_t md_size, block_size, lba_count;
865 : : uint64_t lba;
866 : 0 : struct iovec iov, md_iov;
867 : : int rc;
868 : 0 : struct spdk_dif_ctx_init_ext_opts dif_opts;
869 : :
870 : : /* Set appmask and apptag when PRACT is enabled */
871 [ # # ]: 0 : if (fio_qpair->io_flags & SPDK_NVME_IO_FLAGS_PRACT) {
872 : 0 : fio_req->dif_ctx.apptag_mask = g_spdk_apptag_mask;
873 : 0 : fio_req->dif_ctx.app_tag = g_spdk_apptag;
874 : 0 : return 0;
875 : : }
876 : :
877 : 0 : block_size = spdk_nvme_ns_get_sector_size(ns);
878 : 0 : md_size = spdk_nvme_ns_get_md_size(ns);
879 [ # # ]: 0 : lba = io_u->offset / block_size;
880 [ # # ]: 0 : lba_count = io_u->xfer_buflen / block_size;
881 : :
882 : 0 : dif_opts.size = SPDK_SIZEOF(&dif_opts, dif_pi_format);
883 : 0 : dif_opts.dif_pi_format = SPDK_DIF_PI_FORMAT_16;
884 : 0 : rc = spdk_dif_ctx_init(&fio_req->dif_ctx, block_size, md_size,
885 [ # # ]: 0 : false, fio_qpair->md_start,
886 : 0 : (enum spdk_dif_type)spdk_nvme_ns_get_pi_type(ns),
887 : : fio_qpair->io_flags, lba, g_spdk_apptag_mask, g_spdk_apptag,
888 : : 0, 0, &dif_opts);
889 [ # # ]: 0 : if (rc != 0) {
890 [ # # # # ]: 0 : fprintf(stderr, "Initialization of DIF context failed\n");
891 : 0 : return rc;
892 : : }
893 : :
894 [ # # ]: 0 : if (io_u->ddir != DDIR_WRITE) {
895 : 0 : return 0;
896 : : }
897 : :
898 : 0 : iov.iov_base = io_u->buf;
899 : 0 : iov.iov_len = io_u->xfer_buflen;
900 : 0 : md_iov.iov_base = fio_req->md_buf;
901 : 0 : md_iov.iov_len = spdk_min(md_size * lba_count, g_spdk_md_per_io_size);
902 : 0 : rc = spdk_dix_generate(&iov, 1, &md_iov, lba_count, &fio_req->dif_ctx);
903 [ # # ]: 0 : if (rc < 0) {
904 [ # # # # ]: 0 : fprintf(stderr, "Generation of DIX failed\n");
905 : : }
906 : :
907 : 0 : return rc;
908 : : }
909 : :
910 : : static int
911 : 0 : fio_extended_lba_verify_pi(struct spdk_fio_qpair *fio_qpair, struct io_u *io_u)
912 : : {
913 : 0 : struct spdk_nvme_ns *ns = fio_qpair->ns;
914 : 0 : struct spdk_fio_request *fio_req = io_u->engine_data;
915 : : uint32_t lba_count;
916 : 0 : struct iovec iov;
917 : 0 : struct spdk_dif_error err_blk = {};
918 : : int rc;
919 : :
920 : : /* Do nothing when PRACT is enabled */
921 [ # # ]: 0 : if (fio_qpair->io_flags & SPDK_NVME_IO_FLAGS_PRACT) {
922 : 0 : return 0;
923 : : }
924 : :
925 : 0 : iov.iov_base = io_u->buf;
926 : 0 : iov.iov_len = io_u->xfer_buflen;
927 [ # # ]: 0 : lba_count = io_u->xfer_buflen / spdk_nvme_ns_get_extended_sector_size(ns);
928 : :
929 : 0 : rc = spdk_dif_verify(&iov, 1, lba_count, &fio_req->dif_ctx, &err_blk);
930 [ # # ]: 0 : if (rc != 0) {
931 [ # # ]: 0 : fprintf(stderr, "DIF error detected. type=%d, offset=%" PRIu32 "\n",
932 [ # # ]: 0 : err_blk.err_type, err_blk.err_offset);
933 : : }
934 : :
935 : 0 : return rc;
936 : : }
937 : :
938 : : static int
939 : 0 : fio_separate_md_verify_pi(struct spdk_fio_qpair *fio_qpair, struct io_u *io_u)
940 : : {
941 : 0 : struct spdk_nvme_ns *ns = fio_qpair->ns;
942 : 0 : struct spdk_fio_request *fio_req = io_u->engine_data;
943 : : uint32_t md_size, lba_count;
944 : 0 : struct iovec iov, md_iov;
945 : 0 : struct spdk_dif_error err_blk = {};
946 : : int rc;
947 : :
948 : : /* Do nothing when PRACT is enabled */
949 [ # # ]: 0 : if (fio_qpair->io_flags & SPDK_NVME_IO_FLAGS_PRACT) {
950 : 0 : return 0;
951 : : }
952 : :
953 : 0 : iov.iov_base = io_u->buf;
954 : 0 : iov.iov_len = io_u->xfer_buflen;
955 [ # # ]: 0 : lba_count = io_u->xfer_buflen / spdk_nvme_ns_get_sector_size(ns);
956 : 0 : md_size = spdk_nvme_ns_get_md_size(ns);
957 : 0 : md_iov.iov_base = fio_req->md_buf;
958 : 0 : md_iov.iov_len = spdk_min(md_size * lba_count, g_spdk_md_per_io_size);
959 : :
960 : 0 : rc = spdk_dix_verify(&iov, 1, &md_iov, lba_count, &fio_req->dif_ctx, &err_blk);
961 [ # # ]: 0 : if (rc != 0) {
962 [ # # ]: 0 : fprintf(stderr, "DIX error detected. type=%d, offset=%" PRIu32 "\n",
963 [ # # ]: 0 : err_blk.err_type, err_blk.err_offset);
964 : : }
965 : :
966 : 0 : return rc;
967 : : }
968 : :
969 : : static void
970 : 1811866 : spdk_fio_completion_cb(void *ctx, const struct spdk_nvme_cpl *cpl)
971 : : {
972 : 1811866 : struct spdk_fio_request *fio_req = ctx;
973 : 1811866 : struct spdk_fio_thread *fio_thread = fio_req->fio_thread;
974 : 1811866 : struct spdk_fio_qpair *fio_qpair = fio_req->fio_qpair;
975 : : int rc;
976 : :
977 [ - + - + : 1811866 : if (fio_qpair->nvme_pi_enabled && fio_req->io->ddir == DDIR_READ) {
- - ]
978 [ # # # # ]: 0 : if (fio_qpair->extended_lba) {
979 : 0 : rc = fio_extended_lba_verify_pi(fio_qpair, fio_req->io);
980 : : } else {
981 : 0 : rc = fio_separate_md_verify_pi(fio_qpair, fio_req->io);
982 : : }
983 [ # # ]: 0 : if (rc != 0) {
984 : 0 : fio_req->io->error = abs(rc);
985 : : }
986 : : }
987 : :
988 [ + - - + ]: 1811866 : if (spdk_nvme_cpl_is_error(cpl)) {
989 : 0 : fio_req->io->error = EIO;
990 : : }
991 : :
992 [ - + ]: 1811866 : assert(fio_thread->iocq_count < fio_thread->iocq_size);
993 : 1811866 : fio_thread->iocq[fio_thread->iocq_count++] = fio_req->io;
994 : 1811866 : }
995 : :
996 : : static void
997 : 334528 : spdk_nvme_io_reset_sgl(void *ref, uint32_t sgl_offset)
998 : : {
999 : 334528 : struct spdk_fio_request *fio_req = (struct spdk_fio_request *)ref;
1000 : :
1001 : 334528 : fio_req->iov_offset = sgl_offset;
1002 : 334528 : fio_req->bit_bucket_data_len = 0;
1003 : 334528 : }
1004 : :
1005 : : static int
1006 : 1338112 : spdk_nvme_io_next_sge(void *ref, void **address, uint32_t *length)
1007 : : {
1008 : 1338112 : struct spdk_fio_request *fio_req = (struct spdk_fio_request *)ref;
1009 : 1338112 : struct io_u *io_u = fio_req->io;
1010 : : uint32_t iov_len;
1011 : : uint32_t bit_bucket_len;
1012 : :
1013 : 1338112 : *address = io_u->buf;
1014 : :
1015 [ + + ]: 1338112 : if (fio_req->iov_offset) {
1016 [ - + ]: 1003584 : assert(fio_req->iov_offset <= io_u->xfer_buflen);
1017 : 1003584 : *address += fio_req->iov_offset;
1018 : : }
1019 : :
1020 : 1338112 : iov_len = io_u->xfer_buflen - fio_req->iov_offset;
1021 [ + + ]: 1338112 : if (iov_len > g_spdk_sge_size) {
1022 : 1003584 : iov_len = g_spdk_sge_size;
1023 : : }
1024 : :
1025 [ - + - - ]: 1338112 : if ((fio_req->bit_bucket_data_len < g_spdk_bit_bucket_data_len) && (io_u->ddir == DDIR_READ)) {
1026 [ # # ]: 0 : assert(g_spdk_bit_bucket_data_len < io_u->xfer_buflen);
1027 : 0 : *address = (void *)UINT64_MAX;
1028 : 0 : bit_bucket_len = g_spdk_bit_bucket_data_len - fio_req->bit_bucket_data_len;
1029 [ # # ]: 0 : if (iov_len > bit_bucket_len) {
1030 : 0 : iov_len = bit_bucket_len;
1031 : : }
1032 : 0 : fio_req->bit_bucket_data_len += iov_len;
1033 : : }
1034 : :
1035 : 1338112 : fio_req->iov_offset += iov_len;
1036 : 1338112 : *length = iov_len;
1037 : :
1038 : 1338112 : return 0;
1039 : : }
1040 : :
1041 : : #if FIO_IOOPS_VERSION >= 24
1042 : : typedef enum fio_q_status fio_q_status_t;
1043 : : #else
1044 : : typedef int fio_q_status_t;
1045 : : #endif
1046 : :
1047 : : static fio_q_status_t
1048 : 1811866 : spdk_fio_queue(struct thread_data *td, struct io_u *io_u)
1049 : : {
1050 : 1811866 : int rc = 1;
1051 : 1811866 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
1052 : 1811866 : struct spdk_fio_request *fio_req = io_u->engine_data;
1053 : : struct spdk_fio_qpair *fio_qpair;
1054 : 1811866 : struct spdk_nvme_ns *ns = NULL;
1055 : 1811866 : void *md_buf = NULL;
1056 : 1811866 : struct spdk_dif_ctx *dif_ctx = &fio_req->dif_ctx;
1057 : : #if FIO_HAS_FDP
1058 : 393459 : struct spdk_nvme_ns_cmd_ext_io_opts ext_opts;
1059 : : #endif
1060 : : uint32_t block_size;
1061 : : uint64_t lba;
1062 : : uint32_t lba_count;
1063 : :
1064 : 1811866 : fio_qpair = get_fio_qpair(fio_thread, io_u->file);
1065 [ - + ]: 1811866 : if (fio_qpair == NULL) {
1066 : 0 : return -ENXIO;
1067 : : }
1068 : 1811866 : ns = fio_qpair->ns;
1069 : :
1070 [ - + - + : 1811866 : if (fio_qpair->nvme_pi_enabled && !fio_qpair->extended_lba) {
- - - - ]
1071 : 0 : md_buf = fio_req->md_buf;
1072 : : }
1073 : 1811866 : fio_req->fio_qpair = fio_qpair;
1074 : :
1075 : 1811866 : block_size = _nvme_get_host_buffer_sector_size(ns, fio_qpair->io_flags);
1076 [ - + ]: 1811866 : lba = io_u->offset / block_size;
1077 [ - + ]: 1811866 : lba_count = io_u->xfer_buflen / block_size;
1078 : :
1079 : : #if FIO_HAS_FDP
1080 : : /* Only SGL support for write command with directives */
1081 [ + + - + : 1811866 : if (io_u->ddir == DDIR_WRITE && io_u->dtype && !g_spdk_enable_sgl) {
- - ]
1082 : 0 : log_err("spdk/nvme: queue() directives require SGL to be enabled\n");
1083 : 0 : io_u->error = -EINVAL;
1084 : 0 : return FIO_Q_COMPLETED;
1085 : : }
1086 : : #endif
1087 : :
1088 : : /* TODO: considering situations that fio will randomize and verify io_u */
1089 [ - + - + ]: 1811866 : if (fio_qpair->nvme_pi_enabled) {
1090 [ # # # # ]: 0 : if (fio_qpair->extended_lba) {
1091 : 0 : rc = fio_extended_lba_setup_pi(fio_qpair, io_u);
1092 : : } else {
1093 : 0 : rc = fio_separate_md_setup_pi(fio_qpair, io_u);
1094 : : }
1095 [ # # ]: 0 : if (rc < 0) {
1096 : 0 : io_u->error = -rc;
1097 : 0 : return FIO_Q_COMPLETED;
1098 : : }
1099 : : }
1100 : :
1101 [ + + - ]: 1811866 : switch (io_u->ddir) {
1102 : 933457 : case DDIR_READ:
1103 [ + + ]: 933457 : if (!g_spdk_enable_sgl) {
1104 : 823059 : rc = spdk_nvme_ns_cmd_read_with_md(ns, fio_qpair->qpair, io_u->buf, md_buf, lba, lba_count,
1105 : : spdk_fio_completion_cb, fio_req,
1106 : 823059 : fio_qpair->io_flags, dif_ctx->apptag_mask, dif_ctx->app_tag);
1107 : : } else {
1108 : 110398 : rc = spdk_nvme_ns_cmd_readv_with_md(ns, fio_qpair->qpair, lba,
1109 : : lba_count, spdk_fio_completion_cb, fio_req, fio_qpair->io_flags,
1110 : : spdk_nvme_io_reset_sgl, spdk_nvme_io_next_sge, md_buf,
1111 : 110398 : dif_ctx->apptag_mask, dif_ctx->app_tag);
1112 : : }
1113 : 933457 : break;
1114 : 878409 : case DDIR_WRITE:
1115 [ + + ]: 878409 : if (!g_spdk_enable_sgl) {
1116 [ + + + - ]: 821543 : if (!fio_qpair->zone_append_enabled) {
1117 : 821543 : rc = spdk_nvme_ns_cmd_write_with_md(ns, fio_qpair->qpair, io_u->buf, md_buf, lba,
1118 : : lba_count,
1119 : : spdk_fio_completion_cb, fio_req,
1120 : 821543 : fio_qpair->io_flags, dif_ctx->apptag_mask, dif_ctx->app_tag);
1121 : : } else {
1122 : 0 : uint64_t zslba = fio_offset_to_zslba(io_u->offset, ns);
1123 : 0 : rc = spdk_nvme_zns_zone_append_with_md(ns, fio_qpair->qpair, io_u->buf, md_buf, zslba,
1124 : : lba_count,
1125 : : spdk_fio_completion_cb, fio_req,
1126 : 0 : fio_qpair->io_flags, dif_ctx->apptag_mask, dif_ctx->app_tag);
1127 : : }
1128 : : } else {
1129 [ - + + - ]: 56866 : if (!fio_qpair->zone_append_enabled) {
1130 : : #if FIO_HAS_FDP
1131 [ - + ]: 56866 : if (spdk_unlikely(io_u->dtype)) {
1132 : 0 : ext_opts.size = SPDK_SIZEOF(&ext_opts, cdw13);
1133 : 0 : ext_opts.io_flags = fio_qpair->io_flags | (io_u->dtype << 20);
1134 : 0 : ext_opts.metadata = md_buf;
1135 : 0 : ext_opts.cdw13 = (io_u->dspec << 16);
1136 : 0 : ext_opts.apptag = dif_ctx->app_tag;
1137 : 0 : ext_opts.apptag_mask = dif_ctx->apptag_mask;
1138 : 0 : rc = spdk_nvme_ns_cmd_writev_ext(ns, fio_qpair->qpair, lba, lba_count,
1139 : : spdk_fio_completion_cb, fio_req,
1140 : : spdk_nvme_io_reset_sgl, spdk_nvme_io_next_sge, &ext_opts);
1141 : 0 : break;
1142 : : }
1143 : : #endif
1144 : 56866 : rc = spdk_nvme_ns_cmd_writev_with_md(ns, fio_qpair->qpair, lba,
1145 : : lba_count, spdk_fio_completion_cb, fio_req, fio_qpair->io_flags,
1146 : : spdk_nvme_io_reset_sgl, spdk_nvme_io_next_sge, md_buf,
1147 : 56866 : dif_ctx->apptag_mask, dif_ctx->app_tag);
1148 : : } else {
1149 : 0 : uint64_t zslba = fio_offset_to_zslba(io_u->offset, ns);
1150 : 0 : rc = spdk_nvme_zns_zone_appendv_with_md(ns, fio_qpair->qpair, zslba,
1151 : : lba_count, spdk_fio_completion_cb, fio_req, fio_qpair->io_flags,
1152 : : spdk_nvme_io_reset_sgl, spdk_nvme_io_next_sge, md_buf,
1153 : 0 : dif_ctx->apptag_mask, dif_ctx->app_tag);
1154 : : }
1155 : : }
1156 : 878409 : break;
1157 : 0 : default:
1158 : 0 : assert(false);
1159 : : break;
1160 : : }
1161 : :
1162 : : /* NVMe read/write functions return -ENOMEM if there are no free requests. */
1163 [ - + ]: 1811866 : if (rc == -ENOMEM) {
1164 : 0 : return FIO_Q_BUSY;
1165 : : }
1166 : :
1167 [ - + ]: 1811866 : if (rc != 0) {
1168 : 0 : io_u->error = abs(rc);
1169 : 0 : return FIO_Q_COMPLETED;
1170 : : }
1171 : :
1172 : 1811866 : return FIO_Q_QUEUED;
1173 : : }
1174 : :
1175 : : static struct io_u *
1176 : 1811866 : spdk_fio_event(struct thread_data *td, int event)
1177 : : {
1178 : 1811866 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
1179 : :
1180 [ - + ]: 1811866 : assert(event >= 0);
1181 [ - + ]: 1811866 : assert((unsigned)event < fio_thread->iocq_count);
1182 : 1811866 : return fio_thread->iocq[event];
1183 : : }
1184 : :
1185 : : static int
1186 : 1803813 : spdk_fio_getevents(struct thread_data *td, unsigned int min,
1187 : : unsigned int max, const struct timespec *t)
1188 : : {
1189 : 1803813 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
1190 : 1803813 : struct spdk_fio_qpair *fio_qpair = NULL;
1191 : 392703 : struct timespec t0, t1;
1192 : 1803813 : uint64_t timeout = 0;
1193 : :
1194 [ - + ]: 1803813 : if (t) {
1195 : 0 : timeout = t->tv_sec * 1000000000L + t->tv_nsec;
1196 [ # # ]: 0 : clock_gettime(CLOCK_MONOTONIC_RAW, &t0);
1197 : : }
1198 : :
1199 : 1803813 : fio_thread->iocq_count = 0;
1200 : :
1201 : : /* fetch the next qpair */
1202 [ + + ]: 1803813 : if (fio_thread->fio_qpair_current) {
1203 : 1803783 : fio_qpair = TAILQ_NEXT(fio_thread->fio_qpair_current, link);
1204 : : }
1205 : :
1206 : : for (;;) {
1207 [ + - ]: 23346010 : if (fio_qpair == NULL) {
1208 : 23346010 : fio_qpair = TAILQ_FIRST(&fio_thread->fio_qpair);
1209 : : }
1210 : :
1211 [ + + ]: 44888207 : while (fio_qpair != NULL) {
1212 : : /*
1213 : : * We can be called while spdk_fio_open()s are still
1214 : : * ongoing, in which case, ->qpair can still be NULL.
1215 : : */
1216 [ - + ]: 23346010 : if (fio_qpair->qpair == NULL) {
1217 : 0 : fio_qpair = TAILQ_NEXT(fio_qpair, link);
1218 : 0 : continue;
1219 : : }
1220 : :
1221 : 23346010 : spdk_nvme_qpair_process_completions(fio_qpair->qpair, max - fio_thread->iocq_count);
1222 : :
1223 [ + + ]: 23346010 : if (fio_thread->iocq_count >= min) {
1224 : : /* reset the current handling qpair */
1225 : 1803813 : fio_thread->fio_qpair_current = fio_qpair;
1226 : 1803813 : return fio_thread->iocq_count;
1227 : : }
1228 : :
1229 : 21542197 : fio_qpair = TAILQ_NEXT(fio_qpair, link);
1230 : : }
1231 : :
1232 [ - + ]: 21542197 : if (t) {
1233 : : uint64_t elapse;
1234 : :
1235 [ # # ]: 0 : clock_gettime(CLOCK_MONOTONIC_RAW, &t1);
1236 : 0 : elapse = ((t1.tv_sec - t0.tv_sec) * 1000000000L)
1237 : 0 : + t1.tv_nsec - t0.tv_nsec;
1238 [ # # ]: 0 : if (elapse > timeout) {
1239 : 0 : break;
1240 : : }
1241 : : }
1242 : : }
1243 : :
1244 : : /* reset the current handling qpair */
1245 : 0 : fio_thread->fio_qpair_current = fio_qpair;
1246 : 0 : return fio_thread->iocq_count;
1247 : : }
1248 : :
1249 : : static int
1250 : 0 : spdk_fio_invalidate(struct thread_data *td, struct fio_file *f)
1251 : : {
1252 : : /* TODO: This should probably send a flush to the device, but for now just return successful. */
1253 : 0 : return 0;
1254 : : }
1255 : :
1256 : : #if FIO_HAS_ZBD
1257 : : static int
1258 : 0 : spdk_fio_get_zoned_model(struct thread_data *td, struct fio_file *f, enum zbd_zoned_model *model)
1259 : : {
1260 : 0 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
1261 : 0 : struct spdk_fio_qpair *fio_qpair = NULL;
1262 : 0 : const struct spdk_nvme_zns_ns_data *zns_data = NULL;
1263 : :
1264 [ # # ]: 0 : if (f->filetype != FIO_TYPE_BLOCK) {
1265 : 0 : log_info("spdk/nvme: unsupported filetype: %d\n", f->filetype);
1266 : 0 : return -EINVAL;
1267 : : }
1268 : :
1269 : 0 : fio_qpair = get_fio_qpair(fio_thread, f);
1270 [ # # ]: 0 : if (!fio_qpair) {
1271 : 0 : log_err("spdk/nvme: no ns/qpair or file_name: '%s'\n", f->file_name);
1272 : 0 : return -ENODEV;
1273 : : }
1274 : :
1275 [ # # # # ]: 0 : switch (spdk_nvme_ns_get_csi(fio_qpair->ns)) {
1276 : 0 : case SPDK_NVME_CSI_NVM:
1277 : 0 : *model = ZBD_NONE;
1278 : 0 : return 0;
1279 : :
1280 : 0 : case SPDK_NVME_CSI_KV:
1281 : 0 : log_err("spdk/nvme: KV namespace is currently not supported\n");
1282 : 0 : return -ENOSYS;
1283 : :
1284 : 0 : case SPDK_NVME_CSI_ZNS:
1285 : 0 : zns_data = spdk_nvme_zns_ns_get_data(fio_qpair->ns);
1286 [ # # ]: 0 : if (!zns_data) {
1287 : 0 : log_err("spdk/nvme: file_name: '%s', ZNS is not enabled\n", f->file_name);
1288 : 0 : return -EINVAL;
1289 : : }
1290 : :
1291 : 0 : *model = ZBD_HOST_MANAGED;
1292 : :
1293 : 0 : return 0;
1294 : : }
1295 : :
1296 : 0 : return -EINVAL;
1297 : : }
1298 : :
1299 : : static int
1300 : 0 : spdk_fio_report_zones(struct thread_data *td, struct fio_file *f, uint64_t offset,
1301 : : struct zbd_zone *zbdz, unsigned int nr_zones)
1302 : : {
1303 : 0 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
1304 : 0 : struct spdk_fio_qpair *fio_qpair = NULL;
1305 : 0 : const struct spdk_nvme_zns_ns_data *zns = NULL;
1306 : : struct spdk_nvme_zns_zone_report *report;
1307 : : struct spdk_nvme_qpair *tmp_qpair;
1308 : 0 : uint32_t report_nzones = 0, report_nzones_max, report_nbytes, mdts_nbytes;
1309 : : uint64_t zsze_nbytes, ns_nzones, lba_nbytes;
1310 : 0 : int completed = 0, err;
1311 : :
1312 : 0 : fio_qpair = get_fio_qpair(fio_thread, f);
1313 [ # # ]: 0 : if (!fio_qpair) {
1314 : 0 : log_err("spdk/nvme: no ns/qpair or file_name: '%s'\n", f->file_name);
1315 : 0 : return -ENODEV;
1316 : : }
1317 : 0 : zns = spdk_nvme_zns_ns_get_data(fio_qpair->ns);
1318 [ # # ]: 0 : if (!zns) {
1319 : 0 : log_err("spdk/nvme: file_name: '%s', zns is not enabled\n", f->file_name);
1320 : 0 : return -EINVAL;
1321 : : }
1322 : :
1323 : : /* qpair has not been allocated yet (it gets allocated in spdk_fio_open()).
1324 : : * Create a temporary qpair in order to perform report zones.
1325 : : */
1326 [ # # ]: 0 : assert(!fio_qpair->qpair);
1327 : :
1328 : 0 : tmp_qpair = spdk_nvme_ctrlr_alloc_io_qpair(fio_qpair->fio_ctrlr->ctrlr, NULL, 0);
1329 [ # # ]: 0 : if (!tmp_qpair) {
1330 : 0 : log_err("spdk/nvme: cannot allocate a temporary qpair\n");
1331 : 0 : return -EIO;
1332 : : }
1333 : :
1334 : : /** Retrieve device parameters */
1335 : 0 : mdts_nbytes = spdk_nvme_ns_get_max_io_xfer_size(fio_qpair->ns);
1336 : 0 : lba_nbytes = spdk_nvme_ns_get_sector_size(fio_qpair->ns);
1337 : 0 : zsze_nbytes = spdk_nvme_zns_ns_get_zone_size(fio_qpair->ns);
1338 : 0 : ns_nzones = spdk_nvme_zns_ns_get_num_zones(fio_qpair->ns);
1339 : :
1340 : : /** Allocate report-buffer without exceeding mdts, zbdz-storage, and what is needed */
1341 : 0 : report_nzones_max = (mdts_nbytes - sizeof(*report)) / sizeof(report->descs[0]);
1342 : 0 : report_nzones_max = spdk_min(spdk_min(report_nzones_max, nr_zones), ns_nzones);
1343 : 0 : report_nbytes = sizeof(report->descs[0]) * report_nzones_max + sizeof(*report);
1344 : 0 : report = calloc(1, report_nbytes);
1345 [ # # ]: 0 : if (!report) {
1346 : 0 : log_err("spdk/nvme: failed report_zones(): ENOMEM\n");
1347 : 0 : err = -ENOMEM;
1348 : 0 : goto exit;
1349 : : }
1350 : :
1351 [ # # ]: 0 : err = spdk_nvme_zns_report_zones(fio_qpair->ns, tmp_qpair, report, report_nbytes,
1352 : 0 : offset / lba_nbytes, SPDK_NVME_ZRA_LIST_ALL, true, pcu_cb,
1353 : : &completed);
1354 [ # # # # : 0 : if (err || pcu(tmp_qpair, &completed) || completed < 0) {
# # ]
1355 : 0 : log_err("spdk/nvme: report_zones(): err: %d, cpl: %d\n", err, completed);
1356 [ # # ]: 0 : err = err ? err : -EIO;
1357 : 0 : goto exit;
1358 : : }
1359 [ # # ]: 0 : assert(report->nr_zones <= report_nzones_max);
1360 : 0 : report_nzones = report->nr_zones;
1361 : :
1362 [ # # ]: 0 : for (uint64_t idx = 0; idx < report->nr_zones; ++idx) {
1363 : 0 : struct spdk_nvme_zns_zone_desc *zdesc = &report->descs[idx];
1364 : :
1365 : 0 : zbdz[idx].start = zdesc->zslba * lba_nbytes;
1366 : 0 : zbdz[idx].len = zsze_nbytes;
1367 : 0 : zbdz[idx].capacity = zdesc->zcap * lba_nbytes;
1368 : 0 : zbdz[idx].wp = zdesc->wp * lba_nbytes;
1369 : :
1370 [ # # ]: 0 : switch (zdesc->zt) {
1371 : 0 : case SPDK_NVME_ZONE_TYPE_SEQWR:
1372 : 0 : zbdz[idx].type = ZBD_ZONE_TYPE_SWR;
1373 : 0 : break;
1374 : :
1375 : 0 : default:
1376 : 0 : log_err("spdk/nvme: %s: inv. zone-type: 0x%x\n", f->file_name, zdesc->zt);
1377 : 0 : err = -EIO;
1378 : 0 : goto exit;
1379 : : }
1380 : :
1381 [ # # # # : 0 : switch (zdesc->zs) {
# # # # ]
1382 : 0 : case SPDK_NVME_ZONE_STATE_EMPTY:
1383 : 0 : zbdz[idx].cond = ZBD_ZONE_COND_EMPTY;
1384 : 0 : break;
1385 : 0 : case SPDK_NVME_ZONE_STATE_IOPEN:
1386 : 0 : zbdz[idx].cond = ZBD_ZONE_COND_IMP_OPEN;
1387 : 0 : break;
1388 : 0 : case SPDK_NVME_ZONE_STATE_EOPEN:
1389 : 0 : zbdz[idx].cond = ZBD_ZONE_COND_EXP_OPEN;
1390 : 0 : break;
1391 : 0 : case SPDK_NVME_ZONE_STATE_CLOSED:
1392 : 0 : zbdz[idx].cond = ZBD_ZONE_COND_CLOSED;
1393 : 0 : break;
1394 : 0 : case SPDK_NVME_ZONE_STATE_RONLY:
1395 : 0 : zbdz[idx].cond = ZBD_ZONE_COND_READONLY;
1396 : 0 : break;
1397 : 0 : case SPDK_NVME_ZONE_STATE_FULL:
1398 : 0 : zbdz[idx].cond = ZBD_ZONE_COND_FULL;
1399 : 0 : break;
1400 : 0 : case SPDK_NVME_ZONE_STATE_OFFLINE:
1401 : 0 : zbdz[idx].cond = ZBD_ZONE_COND_OFFLINE;
1402 : 0 : break;
1403 : :
1404 : 0 : default:
1405 : 0 : log_err("spdk/nvme: %s: inv. zone-state: 0x%x\n", f->file_name, zdesc->zs);
1406 : 0 : err = -EIO;
1407 : 0 : goto exit;
1408 : : }
1409 : : }
1410 : :
1411 : 0 : exit:
1412 : 0 : spdk_nvme_ctrlr_free_io_qpair(tmp_qpair);
1413 : 0 : free(report);
1414 : :
1415 [ # # ]: 0 : return err ? err : (int)report_nzones;
1416 : : }
1417 : :
1418 : : static int
1419 : 0 : spdk_fio_reset_wp(struct thread_data *td, struct fio_file *f, uint64_t offset, uint64_t length)
1420 : : {
1421 : 0 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
1422 : 0 : struct spdk_fio_qpair *fio_qpair = NULL;
1423 : 0 : const struct spdk_nvme_zns_ns_data *zns = NULL;
1424 : : uint64_t zsze_nbytes, lba_nbytes;
1425 : 0 : int err = 0;
1426 : :
1427 : 0 : fio_qpair = get_fio_qpair(fio_thread, f);
1428 [ # # ]: 0 : if (!fio_qpair) {
1429 : 0 : log_err("spdk/nvme: no ns/qpair or file_name: '%s'\n", f->file_name);
1430 : 0 : return -ENODEV;
1431 : : }
1432 : 0 : zns = spdk_nvme_zns_ns_get_data(fio_qpair->ns);
1433 [ # # ]: 0 : if (!zns) {
1434 : 0 : log_err("spdk/nvme: file_name: '%s', zns is not enabled\n", f->file_name);
1435 : 0 : return -EINVAL;
1436 : : }
1437 : 0 : zsze_nbytes = spdk_nvme_zns_ns_get_zone_size(fio_qpair->ns);
1438 : 0 : lba_nbytes = spdk_nvme_ns_get_sector_size(fio_qpair->ns);
1439 : :
1440 : : /** check the assumption that offset is valid zone-start lba */
1441 [ # # # # ]: 0 : if (offset % zsze_nbytes) {
1442 : 0 : log_err("spdk/nvme: offset: %zu is not a valid zslba\n", offset);
1443 : 0 : return -EINVAL;
1444 : : }
1445 : :
1446 [ # # ]: 0 : for (uint64_t cur = offset; cur < offset + length; cur += zsze_nbytes) {
1447 : 0 : int completed = 0;
1448 : :
1449 [ # # ]: 0 : err = spdk_nvme_zns_reset_zone(fio_qpair->ns, fio_qpair->qpair, cur / lba_nbytes,
1450 : : false, pcu_cb, &completed);
1451 [ # # # # : 0 : if (err || pcu(fio_qpair->qpair, &completed) || completed < 0) {
# # ]
1452 : 0 : log_err("spdk/nvme: zns_reset_zone(): err: %d, cpl: %d\n", err, completed);
1453 [ # # ]: 0 : err = err ? err : -EIO;
1454 : 0 : break;
1455 : : }
1456 : : }
1457 : :
1458 : 0 : return err;
1459 : : }
1460 : : #endif
1461 : :
1462 : : #if FIO_IOOPS_VERSION >= 30
1463 : : static int
1464 : 0 : spdk_fio_get_max_open_zones(struct thread_data *td, struct fio_file *f,
1465 : : unsigned int *max_open_zones)
1466 : : {
1467 : 0 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
1468 : 0 : struct spdk_fio_qpair *fio_qpair = NULL;
1469 : :
1470 : 0 : fio_qpair = get_fio_qpair(fio_thread, f);
1471 [ # # ]: 0 : if (!fio_qpair) {
1472 : 0 : log_err("spdk/nvme: no ns/qpair or file_name: '%s'\n", f->file_name);
1473 : 0 : return -ENODEV;
1474 : : }
1475 : :
1476 : 0 : *max_open_zones = spdk_nvme_zns_ns_get_max_open_zones(fio_qpair->ns);
1477 : :
1478 : 0 : return 0;
1479 : : }
1480 : : #endif
1481 : :
1482 : : #if FIO_HAS_FDP
1483 : : static int
1484 : 0 : spdk_fio_fdp_fetch_ruhs(struct thread_data *td, struct fio_file *f,
1485 : : struct fio_ruhs_info *fruhs_info)
1486 : : {
1487 : 0 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
1488 : 0 : struct spdk_fio_qpair *fio_qpair = NULL;
1489 : : struct spdk_nvme_qpair *tmp_qpair;
1490 : : struct {
1491 : : struct spdk_nvme_fdp_ruhs ruhs;
1492 : : struct spdk_nvme_fdp_ruhs_desc desc[128];
1493 : 0 : } fdp_ruhs;
1494 : : uint16_t idx;
1495 : 0 : int completed = 0, err;
1496 : :
1497 : 0 : fio_qpair = get_fio_qpair(fio_thread, f);
1498 [ # # ]: 0 : if (!fio_qpair) {
1499 : 0 : log_err("spdk/nvme: no ns/qpair or file_name: '%s'\n", f->file_name);
1500 : 0 : return -ENODEV;
1501 : : }
1502 : :
1503 : : /* qpair has not been allocated yet (it gets allocated in spdk_fio_open()).
1504 : : * Create a temporary qpair in order to perform report zones.
1505 : : */
1506 [ # # ]: 0 : assert(!fio_qpair->qpair);
1507 : :
1508 : 0 : tmp_qpair = spdk_nvme_ctrlr_alloc_io_qpair(fio_qpair->fio_ctrlr->ctrlr, NULL, 0);
1509 [ # # ]: 0 : if (!tmp_qpair) {
1510 : 0 : log_err("spdk/nvme: cannot allocate a temporary qpair\n");
1511 : 0 : return -EIO;
1512 : : }
1513 : :
1514 : 0 : err = spdk_nvme_ns_cmd_io_mgmt_recv(fio_qpair->ns, tmp_qpair, &fdp_ruhs, sizeof(fdp_ruhs),
1515 : : SPDK_NVME_FDP_IO_MGMT_RECV_RUHS, 0, pcu_cb, &completed);
1516 [ # # # # : 0 : if (err || pcu(tmp_qpair, &completed) || completed < 0) {
# # ]
1517 : 0 : log_err("spdk/nvme: fetch_ruhs(): err: %d, cpl: %d\n", err, completed);
1518 [ # # ]: 0 : err = err ? err : -EIO;
1519 : 0 : goto exit;
1520 : : }
1521 : :
1522 : 0 : fruhs_info->nr_ruhs = fdp_ruhs.ruhs.nruhsd;
1523 [ # # ]: 0 : for (idx = 0; idx < fdp_ruhs.ruhs.nruhsd; idx++) {
1524 : 0 : fruhs_info->plis[idx] = fdp_ruhs.desc[idx].pid;
1525 : : }
1526 : :
1527 : 0 : exit:
1528 : 0 : spdk_nvme_ctrlr_free_io_qpair(tmp_qpair);
1529 : :
1530 : 0 : return err;
1531 : : }
1532 : : #endif
1533 : :
1534 : : static void
1535 : 30 : spdk_fio_cleanup(struct thread_data *td)
1536 : : {
1537 : 30 : struct spdk_fio_thread *fio_thread = td->io_ops_data;
1538 : : struct spdk_fio_qpair *fio_qpair, *fio_qpair_tmp;
1539 : 30 : struct spdk_fio_options *fio_options = td->eo;
1540 : :
1541 [ - + ]: 30 : if (fio_options->spdk_tracing) {
1542 : 0 : spdk_trace_unregister_user_thread();
1543 : : }
1544 : :
1545 [ + + ]: 60 : TAILQ_FOREACH_SAFE(fio_qpair, &fio_thread->fio_qpair, link, fio_qpair_tmp) {
1546 [ - + ]: 30 : TAILQ_REMOVE(&fio_thread->fio_qpair, fio_qpair, link);
1547 : 30 : free(fio_qpair);
1548 : : }
1549 : :
1550 : 30 : free(fio_thread->iocq);
1551 : 30 : free(fio_thread);
1552 : :
1553 [ - + ]: 30 : pthread_mutex_lock(&g_mutex);
1554 : 30 : g_td_count--;
1555 [ + - ]: 30 : if (g_td_count == 0) {
1556 : : struct spdk_fio_ctrlr *fio_ctrlr, *fio_ctrlr_tmp;
1557 : 30 : struct spdk_nvme_detach_ctx *detach_ctx = NULL;
1558 : :
1559 [ + + ]: 60 : TAILQ_FOREACH_SAFE(fio_ctrlr, &g_ctrlrs, link, fio_ctrlr_tmp) {
1560 [ - + ]: 30 : TAILQ_REMOVE(&g_ctrlrs, fio_ctrlr, link);
1561 : 30 : spdk_nvme_detach_async(fio_ctrlr->ctrlr, &detach_ctx);
1562 : 30 : free(fio_ctrlr);
1563 : : }
1564 : :
1565 [ + - ]: 30 : if (detach_ctx) {
1566 : 30 : spdk_nvme_detach_poll(detach_ctx);
1567 : : }
1568 : :
1569 [ - + ]: 30 : if (fio_options->enable_vmd) {
1570 : 0 : spdk_vmd_fini();
1571 : : }
1572 : : }
1573 [ - + ]: 30 : pthread_mutex_unlock(&g_mutex);
1574 [ + - ]: 30 : if (TAILQ_EMPTY(&g_ctrlrs)) {
1575 [ + - ]: 30 : if (pthread_cancel(g_ctrlr_thread_id) == 0) {
1576 : 30 : pthread_join(g_ctrlr_thread_id, NULL);
1577 : : }
1578 : : }
1579 : 30 : }
1580 : :
1581 : : /* This function enables addition of SPDK parameters to the fio config
1582 : : * Adding new parameters by defining them here and defining a callback
1583 : : * function to read the parameter value. */
1584 : : static struct fio_option options[] = {
1585 : : {
1586 : : .name = "enable_wrr",
1587 : : .lname = "Enable weighted round robin (WRR) for IO submission queues",
1588 : : .type = FIO_OPT_INT,
1589 : : .off1 = offsetof(struct spdk_fio_options, enable_wrr),
1590 : : .def = "0",
1591 : : .help = "Enable weighted round robin (WRR) for IO submission queues",
1592 : : .category = FIO_OPT_C_ENGINE,
1593 : : .group = FIO_OPT_G_INVALID,
1594 : : },
1595 : : {
1596 : : .name = "arbitration_burst",
1597 : : .lname = "Arbitration Burst",
1598 : : .type = FIO_OPT_INT,
1599 : : .off1 = offsetof(struct spdk_fio_options, arbitration_burst),
1600 : : .def = "0",
1601 : : .help = "Arbitration Burst used for WRR (valid range from 0 - 7)",
1602 : : .category = FIO_OPT_C_ENGINE,
1603 : : .group = FIO_OPT_G_INVALID,
1604 : : },
1605 : : {
1606 : : .name = "low_weight",
1607 : : .lname = "low_weight for WRR",
1608 : : .type = FIO_OPT_INT,
1609 : : .off1 = offsetof(struct spdk_fio_options, low_weight),
1610 : : .def = "0",
1611 : : .help = "low_weight used for WRR (valid range from 0 - 255)",
1612 : : .category = FIO_OPT_C_ENGINE,
1613 : : .group = FIO_OPT_G_INVALID,
1614 : : },
1615 : : {
1616 : : .name = "medium_weight",
1617 : : .lname = "medium_weight for WRR",
1618 : : .type = FIO_OPT_INT,
1619 : : .off1 = offsetof(struct spdk_fio_options, medium_weight),
1620 : : .def = "0",
1621 : : .help = "medium weight used for WRR (valid range from 0 - 255)",
1622 : : .category = FIO_OPT_C_ENGINE,
1623 : : .group = FIO_OPT_G_INVALID,
1624 : : },
1625 : : {
1626 : : .name = "high_weight",
1627 : : .lname = "high_weight for WRR",
1628 : : .type = FIO_OPT_INT,
1629 : : .off1 = offsetof(struct spdk_fio_options, high_weight),
1630 : : .def = "0",
1631 : : .help = "high weight used for WRR (valid range from 0 - 255)",
1632 : : .category = FIO_OPT_C_ENGINE,
1633 : : .group = FIO_OPT_G_INVALID,
1634 : : },
1635 : : {
1636 : : .name = "wrr_priority",
1637 : : .lname = "priority used for WRR",
1638 : : .type = FIO_OPT_INT,
1639 : : .off1 = offsetof(struct spdk_fio_options, wrr_priority),
1640 : : .def = "0",
1641 : : .help = "priority used for WRR (valid range from 0-3)",
1642 : : .category = FIO_OPT_C_ENGINE,
1643 : : .group = FIO_OPT_G_INVALID,
1644 : : },
1645 : : {
1646 : : .name = "mem_size_mb",
1647 : : .lname = "Memory size in MB",
1648 : : .type = FIO_OPT_INT,
1649 : : .off1 = offsetof(struct spdk_fio_options, mem_size),
1650 : : .def = "0",
1651 : : .help = "Memory Size for SPDK (MB)",
1652 : : .category = FIO_OPT_C_ENGINE,
1653 : : .group = FIO_OPT_G_INVALID,
1654 : : },
1655 : : {
1656 : : .name = "shm_id",
1657 : : .lname = "shared memory ID",
1658 : : .type = FIO_OPT_INT,
1659 : : .off1 = offsetof(struct spdk_fio_options, shm_id),
1660 : : .def = "-1",
1661 : : .help = "Shared Memory ID",
1662 : : .category = FIO_OPT_C_ENGINE,
1663 : : .group = FIO_OPT_G_INVALID,
1664 : : },
1665 : : {
1666 : : .name = "enable_sgl",
1667 : : .lname = "SGL used for I/O commands",
1668 : : .type = FIO_OPT_INT,
1669 : : .off1 = offsetof(struct spdk_fio_options, enable_sgl),
1670 : : .def = "0",
1671 : : .help = "SGL Used for I/O Commands (enable_sgl=1 or enable_sgl=0)",
1672 : : .category = FIO_OPT_C_ENGINE,
1673 : : .group = FIO_OPT_G_INVALID,
1674 : : },
1675 : : {
1676 : : .name = "sge_size",
1677 : : .lname = "SGL size used for I/O commands",
1678 : : .type = FIO_OPT_INT,
1679 : : .off1 = offsetof(struct spdk_fio_options, sge_size),
1680 : : .def = "4096",
1681 : : .help = "SGL size in bytes for I/O Commands (default 4096)",
1682 : : .category = FIO_OPT_C_ENGINE,
1683 : : .group = FIO_OPT_G_INVALID,
1684 : : },
1685 : : {
1686 : : .name = "bit_bucket_data_len",
1687 : : .lname = "Amount of data used for Bit Bucket",
1688 : : .type = FIO_OPT_INT,
1689 : : .off1 = offsetof(struct spdk_fio_options, bit_bucket_data_len),
1690 : : .def = "0",
1691 : : .help = "Bit Bucket Data Length for READ commands (disabled by default)",
1692 : : .category = FIO_OPT_C_ENGINE,
1693 : : .group = FIO_OPT_G_INVALID,
1694 : : },
1695 : : {
1696 : : .name = "hostnqn",
1697 : : .lname = "Host NQN to use when connecting to controllers.",
1698 : : .type = FIO_OPT_STR_STORE,
1699 : : .off1 = offsetof(struct spdk_fio_options, hostnqn),
1700 : : .help = "Host NQN",
1701 : : .category = FIO_OPT_C_ENGINE,
1702 : : .group = FIO_OPT_G_INVALID,
1703 : : },
1704 : : {
1705 : : .name = "pi_act",
1706 : : .lname = "Protection Information Action",
1707 : : .type = FIO_OPT_INT,
1708 : : .off1 = offsetof(struct spdk_fio_options, pi_act),
1709 : : .def = "1",
1710 : : .help = "Protection Information Action bit (pi_act=1 or pi_act=0)",
1711 : : .category = FIO_OPT_C_ENGINE,
1712 : : .group = FIO_OPT_G_INVALID,
1713 : : },
1714 : : {
1715 : : .name = "pi_chk",
1716 : : .lname = "Protection Information Check(GUARD|REFTAG|APPTAG)",
1717 : : .type = FIO_OPT_STR_STORE,
1718 : : .off1 = offsetof(struct spdk_fio_options, pi_chk),
1719 : : .def = NULL,
1720 : : .help = "Control of Protection Information Checking (pi_chk=GUARD|REFTAG|APPTAG)",
1721 : : .category = FIO_OPT_C_ENGINE,
1722 : : .group = FIO_OPT_G_INVALID,
1723 : : },
1724 : : {
1725 : : .name = "md_per_io_size",
1726 : : .lname = "Separate Metadata Buffer Size per I/O",
1727 : : .type = FIO_OPT_INT,
1728 : : .off1 = offsetof(struct spdk_fio_options, md_per_io_size),
1729 : : .def = "4096",
1730 : : .help = "Size of separate metadata buffer per I/O (Default: 4096)",
1731 : : .category = FIO_OPT_C_ENGINE,
1732 : : .group = FIO_OPT_G_INVALID,
1733 : : },
1734 : : {
1735 : : .name = "apptag",
1736 : : .lname = "Application Tag used in Protection Information",
1737 : : .type = FIO_OPT_INT,
1738 : : .off1 = offsetof(struct spdk_fio_options, apptag),
1739 : : .def = "0x1234",
1740 : : .help = "Application Tag used in Protection Information field (Default: 0x1234)",
1741 : : .category = FIO_OPT_C_ENGINE,
1742 : : .group = FIO_OPT_G_INVALID,
1743 : : },
1744 : : {
1745 : : .name = "apptag_mask",
1746 : : .lname = "Application Tag Mask",
1747 : : .type = FIO_OPT_INT,
1748 : : .off1 = offsetof(struct spdk_fio_options, apptag_mask),
1749 : : .def = "0xffff",
1750 : : .help = "Application Tag Mask used with Application Tag (Default: 0xffff)",
1751 : : .category = FIO_OPT_C_ENGINE,
1752 : : .group = FIO_OPT_G_INVALID,
1753 : : },
1754 : : {
1755 : : .name = "digest_enable",
1756 : : .lname = "PDU digest choice for NVMe/TCP Transport(NONE|HEADER|DATA|BOTH)",
1757 : : .type = FIO_OPT_STR_STORE,
1758 : : .off1 = offsetof(struct spdk_fio_options, digest_enable),
1759 : : .def = NULL,
1760 : : .help = "Control the NVMe/TCP control(digest_enable=NONE|HEADER|DATA|BOTH)",
1761 : : .category = FIO_OPT_C_ENGINE,
1762 : : .group = FIO_OPT_G_INVALID,
1763 : : },
1764 : : {
1765 : : .name = "enable_vmd",
1766 : : .lname = "Enable VMD enumeration",
1767 : : .type = FIO_OPT_INT,
1768 : : .off1 = offsetof(struct spdk_fio_options, enable_vmd),
1769 : : .def = "0",
1770 : : .help = "Enable VMD enumeration (enable_vmd=1 or enable_vmd=0)",
1771 : : .category = FIO_OPT_C_ENGINE,
1772 : : .group = FIO_OPT_G_INVALID,
1773 : : },
1774 : : {
1775 : : .name = "initial_zone_reset",
1776 : : .lname = "Reset Zones on initialization",
1777 : : .type = FIO_OPT_INT,
1778 : : .off1 = offsetof(struct spdk_fio_options, initial_zone_reset),
1779 : : .def = "0",
1780 : : .help = "Reset Zones on initialization (0=disable, 1=Reset All Zones)",
1781 : : .category = FIO_OPT_C_ENGINE,
1782 : : .group = FIO_OPT_G_INVALID,
1783 : : },
1784 : : {
1785 : : .name = "zone_append",
1786 : : .lname = "Use zone append instead of write",
1787 : : .type = FIO_OPT_INT,
1788 : : .off1 = offsetof(struct spdk_fio_options, zone_append),
1789 : : .def = "0",
1790 : : .help = "Use zone append instead of write (1=zone append, 0=write)",
1791 : : .category = FIO_OPT_C_ENGINE,
1792 : : .group = FIO_OPT_G_INVALID,
1793 : : },
1794 : : {
1795 : : .name = "print_qid_mappings",
1796 : : .lname = "Print job-to-qid mappings",
1797 : : .type = FIO_OPT_INT,
1798 : : .off1 = offsetof(struct spdk_fio_options, print_qid_mappings),
1799 : : .def = "0",
1800 : : .help = "Print job-to-qid mappings (0=disable, 1=enable)",
1801 : : .category = FIO_OPT_C_ENGINE,
1802 : : .group = FIO_OPT_G_INVALID,
1803 : : },
1804 : : {
1805 : : .name = "log_flags",
1806 : : .lname = "log_flags",
1807 : : .type = FIO_OPT_STR_STORE,
1808 : : .off1 = offsetof(struct spdk_fio_options, log_flags),
1809 : : .help = "Enable log flags (comma-separated list)",
1810 : : .category = FIO_OPT_C_ENGINE,
1811 : : .group = FIO_OPT_G_INVALID,
1812 : : },
1813 : : {
1814 : : .name = "spdk_tracing",
1815 : : .lname = "Enable SPDK Tracing",
1816 : : .type = FIO_OPT_INT,
1817 : : .off1 = offsetof(struct spdk_fio_options, spdk_tracing),
1818 : : .def = "0",
1819 : : .help = "SPDK Tracing (0=disable, 1=enable)",
1820 : : .category = FIO_OPT_C_ENGINE,
1821 : : .group = FIO_OPT_G_INVALID,
1822 : : },
1823 : : {
1824 : : .name = NULL,
1825 : : },
1826 : : };
1827 : :
1828 : : /* FIO imports this structure using dlsym */
1829 : : struct ioengine_ops ioengine = {
1830 : : .name = "spdk",
1831 : : .version = FIO_IOOPS_VERSION,
1832 : : .queue = spdk_fio_queue,
1833 : : .getevents = spdk_fio_getevents,
1834 : : .event = spdk_fio_event,
1835 : : .cleanup = spdk_fio_cleanup,
1836 : : .open_file = spdk_fio_open,
1837 : : .close_file = spdk_fio_close,
1838 : : .invalidate = spdk_fio_invalidate,
1839 : : .iomem_alloc = spdk_fio_iomem_alloc,
1840 : : .iomem_free = spdk_fio_iomem_free,
1841 : : .setup = spdk_fio_setup,
1842 : : .init = spdk_fio_init,
1843 : : .io_u_init = spdk_fio_io_u_init,
1844 : : .io_u_free = spdk_fio_io_u_free,
1845 : : #if FIO_HAS_ZBD
1846 : : .get_zoned_model = spdk_fio_get_zoned_model,
1847 : : .report_zones = spdk_fio_report_zones,
1848 : : .reset_wp = spdk_fio_reset_wp,
1849 : : #endif
1850 : : #if FIO_IOOPS_VERSION >= 30
1851 : : .get_max_open_zones = spdk_fio_get_max_open_zones,
1852 : : #endif
1853 : : #if FIO_HAS_FDP
1854 : : .fdp_fetch_ruhs = spdk_fio_fdp_fetch_ruhs,
1855 : : #endif
1856 : : .flags = FIO_RAWIO | FIO_NOEXTEND | FIO_NODISKUTIL | FIO_MEMALIGN | FIO_DISKLESSIO,
1857 : : .options = options,
1858 : : .option_struct_size = sizeof(struct spdk_fio_options),
1859 : : };
1860 : :
1861 : : static void fio_init
1862 : 30 : fio_spdk_register(void)
1863 : : {
1864 : 30 : register_ioengine(&ioengine);
1865 : 30 : }
1866 : :
1867 : : static void fio_exit
1868 : 30 : fio_spdk_unregister(void)
1869 : : {
1870 [ + + + - ]: 30 : if (g_spdk_env_initialized) {
1871 : 30 : spdk_trace_cleanup();
1872 : 30 : spdk_env_fini();
1873 : : }
1874 : :
1875 : 30 : unregister_ioengine(&ioengine);
1876 : 30 : }
1877 : :
1878 : 30 : SPDK_LOG_REGISTER_COMPONENT(fio_nvme)
|