4 * IO engine using the xNVMe C API.
6 * See: http://xnvme.io/
8 * SPDX-License-Identifier: Apache-2.0
13 #include <libxnvme_libconf.h>
14 #include <libxnvme_nvm.h>
15 #include <libxnvme_znd.h>
16 #include <libxnvme_spec_fs.h>
18 #include "zbd_types.h"
21 static pthread_mutex_t g_serialize = PTHREAD_MUTEX_INITIALIZER;
23 struct xnvme_fioe_fwrap {
24 /* fio file representation */
25 struct fio_file *fio_file;
27 /* xNVMe device handle */
28 struct xnvme_dev *dev;
29 /* xNVMe device geometry */
30 const struct xnvme_geo *geo;
32 struct xnvme_queue *queue;
39 XNVME_STATIC_ASSERT(sizeof(struct xnvme_fioe_fwrap) == 64, "Incorrect size")
41 struct xnvme_fioe_data {
42 /* I/O completion queue */
45 /* # of iocq entries; incremented via getevents()/cb_pool() */
49 * # of errors; incremented when observed on completion via
50 * getevents()/cb_pool()
54 /* Controller which device/file to select */
58 /* Number of devices/files for which open() has been called */
60 /* Number of devices/files allocated in files[] */
67 struct xnvme_fioe_fwrap files[];
69 XNVME_STATIC_ASSERT(sizeof(struct xnvme_fioe_data) == 64, "Incorrect size")
71 struct xnvme_fioe_options {
74 unsigned int sqpoll_thread;
75 unsigned int xnvme_dev_nsid;
76 unsigned int xnvme_iovec;
83 static struct fio_option options[] = {
86 .lname = "High Priority",
87 .type = FIO_OPT_STR_SET,
88 .off1 = offsetof(struct xnvme_fioe_options, hipri),
89 .help = "Use polled IO completions",
90 .category = FIO_OPT_C_ENGINE,
91 .group = FIO_OPT_G_XNVME,
94 .name = "sqthread_poll",
95 .lname = "Kernel SQ thread polling",
96 .type = FIO_OPT_STR_SET,
97 .off1 = offsetof(struct xnvme_fioe_options, sqpoll_thread),
98 .help = "Offload submission/completion to kernel thread",
99 .category = FIO_OPT_C_ENGINE,
100 .group = FIO_OPT_G_XNVME,
104 .lname = "xNVMe Backend",
105 .type = FIO_OPT_STR_STORE,
106 .off1 = offsetof(struct xnvme_fioe_options, xnvme_be),
107 .help = "Select xNVMe backend [spdk,linux,fbsd]",
108 .category = FIO_OPT_C_ENGINE,
109 .group = FIO_OPT_G_XNVME,
112 .name = "xnvme_async",
113 .lname = "xNVMe Asynchronous command-interface",
114 .type = FIO_OPT_STR_STORE,
115 .off1 = offsetof(struct xnvme_fioe_options, xnvme_async),
116 .help = "Select xNVMe async. interface: "
117 "[emu,thrpool,io_uring,io_uring_cmd,libaio,posix,vfio,nil]",
118 .category = FIO_OPT_C_ENGINE,
119 .group = FIO_OPT_G_XNVME,
122 .name = "xnvme_sync",
123 .lname = "xNVMe Synchronous. command-interface",
124 .type = FIO_OPT_STR_STORE,
125 .off1 = offsetof(struct xnvme_fioe_options, xnvme_sync),
126 .help = "Select xNVMe sync. interface: [nvme,psync,block]",
127 .category = FIO_OPT_C_ENGINE,
128 .group = FIO_OPT_G_XNVME,
131 .name = "xnvme_admin",
132 .lname = "xNVMe Admin command-interface",
133 .type = FIO_OPT_STR_STORE,
134 .off1 = offsetof(struct xnvme_fioe_options, xnvme_admin),
135 .help = "Select xNVMe admin. cmd-interface: [nvme,block]",
136 .category = FIO_OPT_C_ENGINE,
137 .group = FIO_OPT_G_XNVME,
140 .name = "xnvme_dev_nsid",
141 .lname = "xNVMe Namespace-Identifier, for user-space NVMe driver",
143 .off1 = offsetof(struct xnvme_fioe_options, xnvme_dev_nsid),
144 .help = "xNVMe Namespace-Identifier, for user-space NVMe driver",
145 .category = FIO_OPT_C_ENGINE,
146 .group = FIO_OPT_G_XNVME,
149 .name = "xnvme_iovec",
150 .lname = "Vectored IOs",
151 .type = FIO_OPT_STR_SET,
152 .off1 = offsetof(struct xnvme_fioe_options, xnvme_iovec),
153 .help = "Send vectored IOs",
154 .category = FIO_OPT_C_ENGINE,
155 .group = FIO_OPT_G_XNVME,
163 static void cb_pool(struct xnvme_cmd_ctx *ctx, void *cb_arg)
165 struct io_u *io_u = cb_arg;
166 struct xnvme_fioe_data *xd = io_u->mmap_data;
168 if (xnvme_cmd_ctx_cpl_status(ctx)) {
169 xnvme_cmd_ctx_pr(ctx, XNVME_PR_DEF);
174 xd->iocq[xd->completed++] = io_u;
175 xnvme_queue_put_cmd_ctx(ctx->async.queue, ctx);
178 static struct xnvme_opts xnvme_opts_from_fioe(struct thread_data *td)
180 struct xnvme_fioe_options *o = td->eo;
181 struct xnvme_opts opts = xnvme_opts_default();
183 opts.nsid = o->xnvme_dev_nsid;
184 opts.be = o->xnvme_be;
185 opts.async = o->xnvme_async;
186 opts.sync = o->xnvme_sync;
187 opts.admin = o->xnvme_admin;
189 opts.poll_io = o->hipri;
190 opts.poll_sq = o->sqpoll_thread;
192 opts.direct = td->o.odirect;
197 static void _dev_close(struct thread_data *td, struct xnvme_fioe_fwrap *fwrap)
200 xnvme_queue_term(fwrap->queue);
202 xnvme_dev_close(fwrap->dev);
204 memset(fwrap, 0, sizeof(*fwrap));
207 static void xnvme_fioe_cleanup(struct thread_data *td)
209 struct xnvme_fioe_data *xd = NULL;
212 if (!td->io_ops_data)
215 xd = td->io_ops_data;
217 err = pthread_mutex_lock(&g_serialize);
219 log_err("ioeng->cleanup(): pthread_mutex_lock(), err(%d)\n", err);
220 /* NOTE: not returning here */
222 for (uint64_t i = 0; i < xd->nallocated; ++i)
223 _dev_close(td, &xd->files[i]);
226 err = pthread_mutex_unlock(&g_serialize);
228 log_err("ioeng->cleanup(): pthread_mutex_unlock(), err(%d)\n", err);
234 td->io_ops_data = NULL;
238 * Helper function setting up device handles as addressed by the naming
239 * convention of the given `fio_file` filename.
241 * Checks thread-options for explicit control of asynchronous implementation via
242 * the ``--xnvme_async={thrpool,emu,posix,io_uring,libaio,nil}``.
244 static int _dev_open(struct thread_data *td, struct fio_file *f)
246 struct xnvme_opts opts = xnvme_opts_from_fioe(td);
247 struct xnvme_fioe_data *xd = td->io_ops_data;
248 struct xnvme_fioe_fwrap *fwrap;
252 if (f->fileno > (int)xd->nallocated) {
253 log_err("ioeng->_dev_open(%s): invalid assumption\n", f->file_name);
257 fwrap = &xd->files[f->fileno];
259 err = pthread_mutex_lock(&g_serialize);
261 log_err("ioeng->_dev_open(%s): pthread_mutex_lock(), err(%d)\n", f->file_name,
266 fwrap->dev = xnvme_dev_open(f->file_name, &opts);
268 log_err("ioeng->_dev_open(%s): xnvme_dev_open(), err(%d)\n", f->file_name, errno);
271 fwrap->geo = xnvme_dev_get_geo(fwrap->dev);
273 if (xnvme_queue_init(fwrap->dev, td->o.iodepth, flags, &(fwrap->queue))) {
274 log_err("ioeng->_dev_open(%s): xnvme_queue_init(), err(?)\n", f->file_name);
277 xnvme_queue_set_cb(fwrap->queue, cb_pool, NULL);
279 fwrap->ssw = xnvme_dev_get_ssw(fwrap->dev);
280 fwrap->lba_nbytes = fwrap->geo->lba_nbytes;
283 fwrap->fio_file->filetype = FIO_TYPE_BLOCK;
284 fwrap->fio_file->real_file_size = fwrap->geo->tbytes;
285 fio_file_set_size_known(fwrap->fio_file);
287 err = pthread_mutex_unlock(&g_serialize);
289 log_err("ioeng->_dev_open(%s): pthread_mutex_unlock(), err(%d)\n", f->file_name,
295 xnvme_queue_term(fwrap->queue);
296 xnvme_dev_close(fwrap->dev);
298 err = pthread_mutex_unlock(&g_serialize);
300 log_err("ioeng->_dev_open(%s): pthread_mutex_unlock(), err(%d)\n", f->file_name,
306 static int xnvme_fioe_init(struct thread_data *td)
308 struct xnvme_fioe_data *xd = NULL;
312 if (!td->o.use_thread) {
313 log_err("ioeng->init(): --thread=1 is required\n");
317 /* Allocate xd and iocq */
318 xd = calloc(1, sizeof(*xd) + sizeof(*xd->files) * td->o.nr_files);
320 log_err("ioeng->init(): !calloc(), err(%d)\n", errno);
324 xd->iocq = calloc(td->o.iodepth, sizeof(struct io_u *));
327 log_err("ioeng->init(): !calloc(xd->iocq), err(%d)\n", errno);
331 xd->iovec = calloc(td->o.iodepth, sizeof(*xd->iovec));
335 log_err("ioeng->init(): !calloc(xd->iovec), err(%d)\n", errno);
340 td->io_ops_data = xd;
342 for_each_file(td, f, i)
344 if (_dev_open(td, f)) {
346 * Note: We are not freeing xd, iocq and iovec. This
347 * will be done as part of cleanup routine.
349 log_err("ioeng->init(): failed; _dev_open(%s)\n", f->file_name);
356 if (xd->nallocated != td->o.nr_files) {
357 log_err("ioeng->init(): failed; nallocated != td->o.nr_files\n");
364 /* NOTE: using the first device for buffer-allocators) */
365 static int xnvme_fioe_iomem_alloc(struct thread_data *td, size_t total_mem)
367 struct xnvme_fioe_data *xd = td->io_ops_data;
368 struct xnvme_fioe_fwrap *fwrap = &xd->files[0];
371 log_err("ioeng->iomem_alloc(): failed; no dev-handle\n");
375 td->orig_buffer = xnvme_buf_alloc(fwrap->dev, total_mem);
377 return td->orig_buffer == NULL;
380 /* NOTE: using the first device for buffer-allocators) */
381 static void xnvme_fioe_iomem_free(struct thread_data *td)
383 struct xnvme_fioe_data *xd = NULL;
384 struct xnvme_fioe_fwrap *fwrap = NULL;
386 if (!td->io_ops_data)
389 xd = td->io_ops_data;
390 fwrap = &xd->files[0];
393 log_err("ioeng->iomem_free(): failed no dev-handle\n");
397 xnvme_buf_free(fwrap->dev, td->orig_buffer);
400 static int xnvme_fioe_io_u_init(struct thread_data *td, struct io_u *io_u)
402 io_u->mmap_data = td->io_ops_data;
407 static void xnvme_fioe_io_u_free(struct thread_data *td, struct io_u *io_u)
409 io_u->mmap_data = NULL;
412 static struct io_u *xnvme_fioe_event(struct thread_data *td, int event)
414 struct xnvme_fioe_data *xd = td->io_ops_data;
417 assert((unsigned)event < xd->completed);
419 return xd->iocq[event];
422 static int xnvme_fioe_getevents(struct thread_data *td, unsigned int min, unsigned int max,
423 const struct timespec *t)
425 struct xnvme_fioe_data *xd = td->io_ops_data;
426 struct xnvme_fioe_fwrap *fwrap = NULL;
427 int nfiles = xd->nallocated;
430 if (xd->prev != -1 && ++xd->prev < nfiles) {
431 fwrap = &xd->files[xd->prev];
437 if (fwrap == NULL || xd->cur == nfiles) {
438 fwrap = &xd->files[0];
442 while (fwrap != NULL && xd->cur < nfiles && err >= 0) {
443 err = xnvme_queue_poke(fwrap->queue, max - xd->completed);
452 log_err("ioeng->getevents(): unhandled IO error\n");
457 if (xd->completed >= min) {
459 return xd->completed;
462 fwrap = &xd->files[xd->cur];
477 return xd->completed;
480 static enum fio_q_status xnvme_fioe_queue(struct thread_data *td, struct io_u *io_u)
482 struct xnvme_fioe_data *xd = td->io_ops_data;
483 struct xnvme_fioe_fwrap *fwrap;
484 struct xnvme_cmd_ctx *ctx;
489 bool vectored_io = ((struct xnvme_fioe_options *)td->eo)->xnvme_iovec;
491 fio_ro_check(td, io_u);
493 fwrap = &xd->files[io_u->file->fileno];
494 nsid = xnvme_dev_get_nsid(fwrap->dev);
496 slba = io_u->offset >> fwrap->ssw;
497 nlb = (io_u->xfer_buflen >> fwrap->ssw) - 1;
499 ctx = xnvme_queue_get_cmd_ctx(fwrap->queue);
500 ctx->async.cb_arg = io_u;
502 ctx->cmd.common.nsid = nsid;
503 ctx->cmd.nvm.slba = slba;
504 ctx->cmd.nvm.nlb = nlb;
506 switch (io_u->ddir) {
508 ctx->cmd.common.opcode = XNVME_SPEC_NVM_OPC_READ;
512 ctx->cmd.common.opcode = XNVME_SPEC_NVM_OPC_WRITE;
516 log_err("ioeng->queue(): ENOSYS: %u\n", io_u->ddir);
517 xnvme_queue_put_cmd_ctx(ctx->async.queue, ctx);
519 io_u->error = ENOSYS;
521 return FIO_Q_COMPLETED;
525 xd->iovec[io_u->index].iov_base = io_u->xfer_buf;
526 xd->iovec[io_u->index].iov_len = io_u->xfer_buflen;
528 err = xnvme_cmd_passv(ctx, &xd->iovec[io_u->index], 1, io_u->xfer_buflen, NULL, 0,
531 err = xnvme_cmd_pass(ctx, io_u->xfer_buf, io_u->xfer_buflen, NULL, 0);
539 xnvme_queue_put_cmd_ctx(ctx->async.queue, ctx);
543 log_err("ioeng->queue(): err: '%d'\n", err);
545 xnvme_queue_put_cmd_ctx(ctx->async.queue, ctx);
547 io_u->error = abs(err);
549 return FIO_Q_COMPLETED;
553 static int xnvme_fioe_close(struct thread_data *td, struct fio_file *f)
555 struct xnvme_fioe_data *xd = td->io_ops_data;
557 dprint(FD_FILE, "xnvme close %s -- nopen: %ld\n", f->file_name, xd->nopen);
564 static int xnvme_fioe_open(struct thread_data *td, struct fio_file *f)
566 struct xnvme_fioe_data *xd = td->io_ops_data;
568 dprint(FD_FILE, "xnvme open %s -- nopen: %ld\n", f->file_name, xd->nopen);
570 if (f->fileno > (int)xd->nallocated) {
571 log_err("ioeng->open(): f->fileno > xd->nallocated; invalid assumption\n");
574 if (xd->files[f->fileno].fio_file != f) {
575 log_err("ioeng->open(): fio_file != f; invalid assumption\n");
584 static int xnvme_fioe_invalidate(struct thread_data *td, struct fio_file *f)
586 /* Consider only doing this with be:spdk */
590 static int xnvme_fioe_get_max_open_zones(struct thread_data *td, struct fio_file *f,
591 unsigned int *max_open_zones)
593 struct xnvme_opts opts = xnvme_opts_from_fioe(td);
594 struct xnvme_dev *dev;
595 const struct xnvme_spec_znd_idfy_ns *zns;
596 int err = 0, err_lock;
598 if (f->filetype != FIO_TYPE_FILE && f->filetype != FIO_TYPE_BLOCK &&
599 f->filetype != FIO_TYPE_CHAR) {
600 log_info("ioeng->get_max_open_zoned(): ignoring filetype: %d\n", f->filetype);
603 err_lock = pthread_mutex_lock(&g_serialize);
605 log_err("ioeng->get_max_open_zones(): pthread_mutex_lock(), err(%d)\n", err_lock);
609 dev = xnvme_dev_open(f->file_name, &opts);
611 log_err("ioeng->get_max_open_zones(): xnvme_dev_open(), err(%d)\n", err_lock);
615 if (xnvme_dev_get_geo(dev)->type != XNVME_GEO_ZONED) {
621 zns = (void *)xnvme_dev_get_ns_css(dev);
623 log_err("ioeng->get_max_open_zones(): xnvme_dev_get_ns_css(), err(%d)\n", errno);
629 * intentional overflow as the value is zero-based and NVMe
630 * defines 0xFFFFFFFF as unlimited thus overflowing to 0 which
631 * is how fio indicates unlimited and otherwise just converting
634 *max_open_zones = zns->mor + 1;
637 xnvme_dev_close(dev);
638 err_lock = pthread_mutex_unlock(&g_serialize);
640 log_err("ioeng->get_max_open_zones(): pthread_mutex_unlock(), err(%d)\n",
647 * Currently, this function is called before of I/O engine initialization, so,
648 * we cannot consult the file-wrapping done when 'fioe' initializes.
649 * Instead we just open based on the given filename.
651 * TODO: unify the different setup methods, consider keeping the handle around,
652 * and consider how to support the --be option in this usecase
654 static int xnvme_fioe_get_zoned_model(struct thread_data *td, struct fio_file *f,
655 enum zbd_zoned_model *model)
657 struct xnvme_opts opts = xnvme_opts_from_fioe(td);
658 struct xnvme_dev *dev;
659 int err = 0, err_lock;
661 if (f->filetype != FIO_TYPE_FILE && f->filetype != FIO_TYPE_BLOCK &&
662 f->filetype != FIO_TYPE_CHAR) {
663 log_info("ioeng->get_zoned_model(): ignoring filetype: %d\n", f->filetype);
667 err = pthread_mutex_lock(&g_serialize);
669 log_err("ioeng->get_zoned_model(): pthread_mutex_lock(), err(%d)\n", err);
673 dev = xnvme_dev_open(f->file_name, &opts);
675 log_err("ioeng->get_zoned_model(): xnvme_dev_open(%s) failed, errno: %d\n",
676 f->file_name, errno);
681 switch (xnvme_dev_get_geo(dev)->type) {
682 case XNVME_GEO_UNKNOWN:
683 dprint(FD_ZBD, "%s: got 'unknown', assigning ZBD_NONE\n", f->file_name);
687 case XNVME_GEO_CONVENTIONAL:
688 dprint(FD_ZBD, "%s: got 'conventional', assigning ZBD_NONE\n", f->file_name);
692 case XNVME_GEO_ZONED:
693 dprint(FD_ZBD, "%s: got 'zoned', assigning ZBD_HOST_MANAGED\n", f->file_name);
694 *model = ZBD_HOST_MANAGED;
698 dprint(FD_ZBD, "%s: hit-default, assigning ZBD_NONE\n", f->file_name);
706 xnvme_dev_close(dev);
708 err_lock = pthread_mutex_unlock(&g_serialize);
710 log_err("ioeng->get_zoned_model(): pthread_mutex_unlock(), err(%d)\n", err_lock);
716 * Fills the given ``zbdz`` with at most ``nr_zones`` zone-descriptors.
718 * The implementation converts the NVMe Zoned Command Set log-pages for Zone
719 * descriptors into the Linux Kernel Zoned Block Report format.
721 * NOTE: This function is called before I/O engine initialization, that is,
722 * before ``_dev_open`` has been called and file-wrapping is setup. Thus is has
723 * to do the ``_dev_open`` itself, and shut it down again once it is done
724 * retrieving the log-pages and converting them to the report format.
726 * TODO: unify the different setup methods, consider keeping the handle around,
727 * and consider how to support the --async option in this usecase
729 static int xnvme_fioe_report_zones(struct thread_data *td, struct fio_file *f, uint64_t offset,
730 struct zbd_zone *zbdz, unsigned int nr_zones)
732 struct xnvme_opts opts = xnvme_opts_from_fioe(td);
733 const struct xnvme_spec_znd_idfy_lbafe *lbafe = NULL;
734 struct xnvme_dev *dev = NULL;
735 const struct xnvme_geo *geo = NULL;
736 struct xnvme_znd_report *rprt = NULL;
739 unsigned int limit = 0;
740 int err = 0, err_lock;
742 dprint(FD_ZBD, "%s: report_zones() offset: %zu, nr_zones: %u\n", f->file_name, offset,
745 err = pthread_mutex_lock(&g_serialize);
747 log_err("ioeng->report_zones(%s): pthread_mutex_lock(), err(%d)\n", f->file_name,
752 dev = xnvme_dev_open(f->file_name, &opts);
754 log_err("ioeng->report_zones(%s): xnvme_dev_open(), err(%d)\n", f->file_name,
759 geo = xnvme_dev_get_geo(dev);
760 ssw = xnvme_dev_get_ssw(dev);
761 lbafe = xnvme_znd_dev_get_lbafe(dev);
763 limit = nr_zones > geo->nzone ? geo->nzone : nr_zones;
765 dprint(FD_ZBD, "%s: limit: %u\n", f->file_name, limit);
767 slba = ((offset >> ssw) / geo->nsect) * geo->nsect;
769 rprt = xnvme_znd_report_from_dev(dev, slba, limit, 0);
771 log_err("ioeng->report_zones(%s): xnvme_znd_report_from_dev(), err(%d)\n",
772 f->file_name, errno);
776 if (rprt->nentries != limit) {
777 log_err("ioeng->report_zones(%s): nentries != nr_zones\n", f->file_name);
781 if (offset > geo->tbytes) {
782 log_err("ioeng->report_zones(%s): out-of-bounds\n", f->file_name);
786 /* Transform the zone-report */
787 for (uint32_t idx = 0; idx < rprt->nentries; ++idx) {
788 struct xnvme_spec_znd_descr *descr = XNVME_ZND_REPORT_DESCR(rprt, idx);
790 zbdz[idx].start = descr->zslba << ssw;
791 zbdz[idx].len = lbafe->zsze << ssw;
792 zbdz[idx].capacity = descr->zcap << ssw;
793 zbdz[idx].wp = descr->wp << ssw;
796 case XNVME_SPEC_ZND_TYPE_SEQWR:
797 zbdz[idx].type = ZBD_ZONE_TYPE_SWR;
801 log_err("ioeng->report_zones(%s): invalid type for zone at offset(%zu)\n",
802 f->file_name, zbdz[idx].start);
808 case XNVME_SPEC_ZND_STATE_EMPTY:
809 zbdz[idx].cond = ZBD_ZONE_COND_EMPTY;
811 case XNVME_SPEC_ZND_STATE_IOPEN:
812 zbdz[idx].cond = ZBD_ZONE_COND_IMP_OPEN;
814 case XNVME_SPEC_ZND_STATE_EOPEN:
815 zbdz[idx].cond = ZBD_ZONE_COND_EXP_OPEN;
817 case XNVME_SPEC_ZND_STATE_CLOSED:
818 zbdz[idx].cond = ZBD_ZONE_COND_CLOSED;
820 case XNVME_SPEC_ZND_STATE_FULL:
821 zbdz[idx].cond = ZBD_ZONE_COND_FULL;
824 case XNVME_SPEC_ZND_STATE_RONLY:
825 case XNVME_SPEC_ZND_STATE_OFFLINE:
827 zbdz[idx].cond = ZBD_ZONE_COND_OFFLINE;
833 xnvme_buf_virt_free(rprt);
835 xnvme_dev_close(dev);
837 err_lock = pthread_mutex_unlock(&g_serialize);
839 log_err("ioeng->report_zones(): pthread_mutex_unlock(), err: %d\n", err_lock);
841 dprint(FD_ZBD, "err: %d, nr_zones: %d\n", err, (int)nr_zones);
843 return err ? err : (int)limit;
847 * NOTE: This function may get called before I/O engine initialization, that is,
848 * before ``_dev_open`` has been called and file-wrapping is setup. In such
849 * case it has to do ``_dev_open`` itself, and shut it down again once it is
850 * done resetting write pointer of zones.
852 static int xnvme_fioe_reset_wp(struct thread_data *td, struct fio_file *f, uint64_t offset,
855 struct xnvme_opts opts = xnvme_opts_from_fioe(td);
856 struct xnvme_fioe_data *xd = NULL;
857 struct xnvme_fioe_fwrap *fwrap = NULL;
858 struct xnvme_dev *dev = NULL;
859 const struct xnvme_geo *geo = NULL;
860 uint64_t first, last;
863 int err = 0, err_lock;
865 if (td->io_ops_data) {
866 xd = td->io_ops_data;
867 fwrap = &xd->files[f->fileno];
876 err = pthread_mutex_lock(&g_serialize);
878 log_err("ioeng->reset_wp(): pthread_mutex_lock(), err(%d)\n", err);
882 dev = xnvme_dev_open(f->file_name, &opts);
884 log_err("ioeng->reset_wp(): xnvme_dev_open(%s) failed, errno(%d)\n",
885 f->file_name, errno);
888 geo = xnvme_dev_get_geo(dev);
889 ssw = xnvme_dev_get_ssw(dev);
892 nsid = xnvme_dev_get_nsid(dev);
894 first = ((offset >> ssw) / geo->nsect) * geo->nsect;
895 last = (((offset + length) >> ssw) / geo->nsect) * geo->nsect;
896 dprint(FD_ZBD, "first: 0x%lx, last: 0x%lx\n", first, last);
898 for (uint64_t zslba = first; zslba < last; zslba += geo->nsect) {
899 struct xnvme_cmd_ctx ctx = xnvme_cmd_ctx_from_dev(dev);
901 if (zslba >= (geo->nsect * geo->nzone)) {
902 log_err("ioeng->reset_wp(): out-of-bounds\n");
907 err = xnvme_znd_mgmt_send(&ctx, nsid, zslba, false,
908 XNVME_SPEC_ZND_CMD_MGMT_SEND_RESET, 0x0, NULL);
909 if (err || xnvme_cmd_ctx_cpl_status(&ctx)) {
910 err = err ? err : -EIO;
911 log_err("ioeng->reset_wp(): err(%d), sc(%d)", err, ctx.cpl.status.sc);
917 if (!td->io_ops_data) {
918 xnvme_dev_close(dev);
920 err_lock = pthread_mutex_unlock(&g_serialize);
922 log_err("ioeng->reset_wp(): pthread_mutex_unlock(), err(%d)\n", err_lock);
928 static int xnvme_fioe_get_file_size(struct thread_data *td, struct fio_file *f)
930 struct xnvme_opts opts = xnvme_opts_from_fioe(td);
931 struct xnvme_dev *dev;
934 if (fio_file_size_known(f))
937 ret = pthread_mutex_lock(&g_serialize);
939 log_err("ioeng->reset_wp(): pthread_mutex_lock(), err(%d)\n", ret);
943 dev = xnvme_dev_open(f->file_name, &opts);
945 log_err("%s: failed retrieving device handle, errno: %d\n", f->file_name, errno);
950 f->real_file_size = xnvme_dev_get_geo(dev)->tbytes;
951 fio_file_set_size_known(f);
952 f->filetype = FIO_TYPE_BLOCK;
955 xnvme_dev_close(dev);
956 err = pthread_mutex_unlock(&g_serialize);
958 log_err("ioeng->reset_wp(): pthread_mutex_unlock(), err(%d)\n", err);
963 FIO_STATIC struct ioengine_ops ioengine = {
965 .version = FIO_IOOPS_VERSION,
967 .option_struct_size = sizeof(struct xnvme_fioe_options),
968 .flags = FIO_DISKLESSIO | FIO_NODISKUTIL | FIO_NOEXTEND | FIO_MEMALIGN | FIO_RAWIO,
970 .cleanup = xnvme_fioe_cleanup,
971 .init = xnvme_fioe_init,
973 .iomem_free = xnvme_fioe_iomem_free,
974 .iomem_alloc = xnvme_fioe_iomem_alloc,
976 .io_u_free = xnvme_fioe_io_u_free,
977 .io_u_init = xnvme_fioe_io_u_init,
979 .event = xnvme_fioe_event,
980 .getevents = xnvme_fioe_getevents,
981 .queue = xnvme_fioe_queue,
983 .close_file = xnvme_fioe_close,
984 .open_file = xnvme_fioe_open,
985 .get_file_size = xnvme_fioe_get_file_size,
987 .invalidate = xnvme_fioe_invalidate,
988 .get_max_open_zones = xnvme_fioe_get_max_open_zones,
989 .get_zoned_model = xnvme_fioe_get_zoned_model,
990 .report_zones = xnvme_fioe_report_zones,
991 .reset_wp = xnvme_fioe_reset_wp,
994 static void fio_init fio_xnvme_register(void)
996 register_ioengine(&ioengine);
999 static void fio_exit fio_xnvme_unregister(void)
1001 unregister_ioengine(&ioengine);