This patch enables support for protection information to xnvme ioengine. This adds 4 new ioengine specific options * pi_act - Protection information action. Default: 1 * pi_chk - Can be set to GUARD, APPTAG or REFTAG * apptag - Sets apptag field of command dword 15 * apptag_mask - Sets apptag_mask field of command dword 15 For the sake of consistency these options are the same as the ones used by io_uring_cmd ioengine and SPDK's external ioengine. Signed-off-by: Ankit Kumar <ankit.kumar@xxxxxxxxxxx> --- HOWTO.rst | 8 +-- engines/xnvme.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++ fio.1 | 8 +-- 3 files changed, 153 insertions(+), 8 deletions(-) diff --git a/HOWTO.rst b/HOWTO.rst index 443a667c..f1a33e7f 100644 --- a/HOWTO.rst +++ b/HOWTO.rst @@ -2495,7 +2495,7 @@ with the caveat that when used on the command line, they must come after the Size in bytes for separate metadata buffer per IO. Default: 0. -.. option:: pi_act=int : [io_uring_cmd] +.. option:: pi_act=int : [io_uring_cmd] [xnvme] Action to take when nvme namespace is formatted with protection information. If this is set to 1 and namespace is formatted with @@ -2511,7 +2511,7 @@ with the caveat that when used on the command line, they must come after the it will use the default slower generator. (see: https://github.com/intel/isa-l) -.. option:: pi_chk=str[,str][,str] : [io_uring_cmd] +.. option:: pi_chk=str[,str][,str] : [io_uring_cmd] [xnvme] Controls the protection information check. This can take one or more of these values. Default: none. @@ -2524,12 +2524,12 @@ with the caveat that when used on the command line, they must come after the **APPTAG** Enables protection information checking of application tag field. -.. option:: apptag=int : [io_uring_cmd] +.. option:: apptag=int : [io_uring_cmd] [xnvme] Specifies logical block application tag value, if namespace is formatted to use end to end protection information. Default: 0x1234. -.. option:: apptag_mask=int : [io_uring_cmd] +.. option:: apptag_mask=int : [io_uring_cmd] [xnvme] Specifies logical block application tag mask value, if namespace is formatted to use end to end protection information. Default: 0xffff. diff --git a/engines/xnvme.c b/engines/xnvme.c index da32678d..c726b805 100644 --- a/engines/xnvme.c +++ b/engines/xnvme.c @@ -67,6 +67,9 @@ struct xnvme_fioe_data { XNVME_STATIC_ASSERT(sizeof(struct xnvme_fioe_data) == 64, "Incorrect size") struct xnvme_fioe_request { + /* Context for NVMe PI */ + struct xnvme_pi_ctx pi_ctx; + /* Separate metadata buffer pointer */ void *md_buf; }; @@ -78,6 +81,10 @@ struct xnvme_fioe_options { unsigned int xnvme_dev_nsid; unsigned int xnvme_iovec; unsigned int md_per_io_size; + unsigned int pi_act; + unsigned int apptag; + unsigned int apptag_mask; + unsigned int prchk; char *xnvme_be; char *xnvme_mem; char *xnvme_async; @@ -86,6 +93,20 @@ struct xnvme_fioe_options { char *xnvme_dev_subnqn; }; +static int str_pi_chk_cb(void *data, const char *str) +{ + struct xnvme_fioe_options *o = data; + + if (strstr(str, "GUARD") != NULL) + o->prchk = XNVME_PI_FLAGS_GUARD_CHECK; + if (strstr(str, "REFTAG") != NULL) + o->prchk |= XNVME_PI_FLAGS_REFTAG_CHECK; + if (strstr(str, "APPTAG") != NULL) + o->prchk |= XNVME_PI_FLAGS_APPTAG_CHECK; + + return 0; +} + static struct fio_option options[] = { { .name = "hipri", @@ -188,6 +209,46 @@ static struct fio_option options[] = { .category = FIO_OPT_C_ENGINE, .group = FIO_OPT_G_XNVME, }, + { + .name = "pi_act", + .lname = "Protection Information Action", + .type = FIO_OPT_BOOL, + .off1 = offsetof(struct xnvme_fioe_options, pi_act), + .def = "1", + .help = "Protection Information Action bit (pi_act=1 or pi_act=0)", + .category = FIO_OPT_C_ENGINE, + .group = FIO_OPT_G_XNVME, + }, + { + .name = "pi_chk", + .lname = "Protection Information Check", + .type = FIO_OPT_STR_STORE, + .def = NULL, + .help = "Control of Protection Information Checking (pi_chk=GUARD,REFTAG,APPTAG)", + .cb = str_pi_chk_cb, + .category = FIO_OPT_C_ENGINE, + .group = FIO_OPT_G_XNVME, + }, + { + .name = "apptag", + .lname = "Application Tag used in Protection Information", + .type = FIO_OPT_INT, + .off1 = offsetof(struct xnvme_fioe_options, apptag), + .def = "0x1234", + .help = "Application Tag used in Protection Information field (Default: 0x1234)", + .category = FIO_OPT_C_ENGINE, + .group = FIO_OPT_G_XNVME, + }, + { + .name = "apptag_mask", + .lname = "Application Tag Mask", + .type = FIO_OPT_INT, + .off1 = offsetof(struct xnvme_fioe_options, apptag_mask), + .def = "0xffff", + .help = "Application Tag Mask used with Application Tag (Default: 0xffff)", + .category = FIO_OPT_C_ENGINE, + .group = FIO_OPT_G_XNVME, + }, { .name = NULL, @@ -198,6 +259,10 @@ static void cb_pool(struct xnvme_cmd_ctx *ctx, void *cb_arg) { struct io_u *io_u = cb_arg; struct xnvme_fioe_data *xd = io_u->mmap_data; + struct xnvme_fioe_request *fio_req = io_u->engine_data; + struct xnvme_fioe_fwrap *fwrap = &xd->files[io_u->file->fileno]; + bool pi_act = (fio_req->pi_ctx.pi_flags >> 3); + int err; if (xnvme_cmd_ctx_cpl_status(ctx)) { xnvme_cmd_ctx_pr(ctx, XNVME_PR_DEF); @@ -205,6 +270,15 @@ static void cb_pool(struct xnvme_cmd_ctx *ctx, void *cb_arg) io_u->error = EIO; } + if (!io_u->error && fwrap->geo->pi_type && (io_u->ddir == DDIR_READ) && !pi_act) { + err = xnvme_pi_verify(&fio_req->pi_ctx, io_u->xfer_buf, + fio_req->md_buf, io_u->xfer_buflen / fwrap->lba_nbytes); + if (err) { + xd->ecount += 1; + io_u->error = EIO; + } + } + xd->iocq[xd->completed++] = io_u; xnvme_queue_put_cmd_ctx(ctx->async.queue, ctx); } @@ -281,6 +355,7 @@ static void xnvme_fioe_cleanup(struct thread_data *td) static int _dev_open(struct thread_data *td, struct fio_file *f) { struct xnvme_opts opts = xnvme_opts_from_fioe(td); + struct xnvme_fioe_options *o = td->eo; struct xnvme_fioe_data *xd = td->io_ops_data; struct xnvme_fioe_fwrap *fwrap; int flags = 0; @@ -322,6 +397,20 @@ static int _dev_open(struct thread_data *td, struct fio_file *f) else fwrap->lba_pow2 = 1; + /* + * When PI action is set and PI size is equal to metadata size, the + * controller inserts/removes PI. So update the LBA data and metadata + * sizes accordingly. + */ + if (o->pi_act && fwrap->geo->pi_type && + fwrap->geo->nbytes_oob == xnvme_pi_size(fwrap->geo->pi_format)) { + if (fwrap->geo->lba_extended) { + fwrap->lba_nbytes -= fwrap->geo->nbytes_oob; + fwrap->lba_pow2 = 1; + } + fwrap->md_nbytes = 0; + } + fwrap->fio_file = f; fwrap->fio_file->filetype = FIO_TYPE_BLOCK; fwrap->fio_file->real_file_size = fwrap->geo->tbytes; @@ -585,6 +674,7 @@ static int xnvme_fioe_getevents(struct thread_data *td, unsigned int min, unsign static enum fio_q_status xnvme_fioe_queue(struct thread_data *td, struct io_u *io_u) { struct xnvme_fioe_data *xd = td->io_ops_data; + struct xnvme_fioe_options *o = td->eo; struct xnvme_fioe_fwrap *fwrap; struct xnvme_cmd_ctx *ctx; struct xnvme_fioe_request *fio_req = io_u->engine_data; @@ -637,6 +727,61 @@ static enum fio_q_status xnvme_fioe_queue(struct thread_data *td, struct io_u *i return FIO_Q_COMPLETED; } + if (fwrap->geo->pi_type && !o->pi_act) { + err = xnvme_pi_ctx_init(&fio_req->pi_ctx, fwrap->lba_nbytes, + fwrap->geo->nbytes_oob, fwrap->geo->lba_extended, + fwrap->geo->pi_loc, fwrap->geo->pi_type, + (o->pi_act << 3 | o->prchk), slba, o->apptag_mask, + o->apptag, fwrap->geo->pi_format); + if (err) { + log_err("ioeng->queue(): err: '%d'\n", err); + + xnvme_queue_put_cmd_ctx(ctx->async.queue, ctx); + + io_u->error = abs(err); + return FIO_Q_COMPLETED; + } + + if (io_u->ddir == DDIR_WRITE) + xnvme_pi_generate(&fio_req->pi_ctx, io_u->xfer_buf, fio_req->md_buf, + nlb + 1); + } + + if (fwrap->geo->pi_type) + ctx->cmd.nvm.prinfo = (o->pi_act << 3 | o->prchk); + + switch (fwrap->geo->pi_type) { + case XNVME_PI_TYPE1: + case XNVME_PI_TYPE2: + switch (fwrap->geo->pi_format) { + case XNVME_SPEC_NVM_NS_16B_GUARD: + if (o->prchk & XNVME_PI_FLAGS_REFTAG_CHECK) + ctx->cmd.nvm.ilbrt = (uint32_t)slba; + break; + case XNVME_SPEC_NVM_NS_64B_GUARD: + if (o->prchk & XNVME_PI_FLAGS_REFTAG_CHECK) { + ctx->cmd.nvm.ilbrt = (uint32_t)slba; + ctx->cmd.common.cdw03 = ((slba >> 32) & 0xffff); + } + break; + default: + break; + } + if (o->prchk & XNVME_PI_FLAGS_APPTAG_CHECK) { + ctx->cmd.nvm.lbat = o->apptag; + ctx->cmd.nvm.lbatm = o->apptag_mask; + } + break; + case XNVME_PI_TYPE3: + if (o->prchk & XNVME_PI_FLAGS_APPTAG_CHECK) { + ctx->cmd.nvm.lbat = o->apptag; + ctx->cmd.nvm.lbatm = o->apptag_mask; + } + break; + case XNVME_PI_DISABLE: + break; + } + if (vectored_io) { xd->iovec[io_u->index].iov_base = io_u->xfer_buf; xd->iovec[io_u->index].iov_len = io_u->xfer_buflen; diff --git a/fio.1 b/fio.1 index 6066b9bf..0448db05 100644 --- a/fio.1 +++ b/fio.1 @@ -2254,7 +2254,7 @@ identifier only at indices 0, 2 and 5 specify, you would set `fdp_pli=0,2,5`. .BI (io_uring_cmd,xnvme)md_per_io_size \fR=\fPint Size in bytes for separate metadata buffer per IO. Default: 0. .TP -.BI (io_uring_cmd)pi_act \fR=\fPint +.BI (io_uring_cmd,xnvme)pi_act \fR=\fPint Action to take when nvme namespace is formatted with protection information. If this is set to 1 and namespace is formatted with metadata size equal to protection information size, fio won't use separate metadata buffer or extended @@ -2268,7 +2268,7 @@ For 16 bit CRC generation fio will use isa-l if available otherwise it will use the default slower generator. (see: https://github.com/intel/isa-l) .TP -.BI (io_uring_cmd)pi_chk \fR=\fPstr[,str][,str] +.BI (io_uring_cmd,xnvme)pi_chk \fR=\fPstr[,str][,str] Controls the protection information check. This can take one or more of these values. Default: none. .RS @@ -2285,11 +2285,11 @@ Enables protection information checking of application tag field. .RE .RE .TP -.BI (io_uring_cmd)apptag \fR=\fPint +.BI (io_uring_cmd,xnvme)apptag \fR=\fPint Specifies logical block application tag value, if namespace is formatted to use end to end protection information. Default: 0x1234. .TP -.BI (io_uring_cmd)apptag_mask \fR=\fPint +.BI (io_uring_cmd,xnvme)apptag_mask \fR=\fPint Specifies logical block application tag mask value, if namespace is formatted to use end to end protection information. Default: 0xffff. .TP -- 2.25.1