engines:xnvme: add support for end to end data protection
authorAnkit Kumar <ankit.kumar@samsung.com>
Tue, 13 Feb 2024 15:33:13 +0000 (21:03 +0530)
committerVincent Fu <vincent.fu@samsung.com>
Tue, 13 Feb 2024 19:24:59 +0000 (14:24 -0500)
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@samsung.com>
Reviewed-by: Jens Axboe <axboe@kernel.dk>
Link: https://lore.kernel.org/r/20240213153315.134202-4-ankit.kumar@samsung.com
Signed-off-by: Vincent Fu <vincent.fu@samsung.com>
HOWTO.rst
engines/xnvme.c
fio.1

index 04b055d948ea19b1e1d33e43cb7c69e96a060667..5bc1713cfd5bc89aa2d425a35c14f038f081ea7a 100644 (file)
--- 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.
index da32678dd91ceeb05efdff1bb6d0a9ddbcab439d..c726b8058c77f3d783aff69a33dba68af43c40d7 100644 (file)
@@ -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 437fbebcfcb7c8fff1ec1d9181f481e3014831cf..7ec5c745a5cea81a3baac0dca1c9d2fb24334450 100644 (file)
--- 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