]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
nvme-cli: add nvme directive command support
authorKwan (Hingkwan) Huen-SSI <kwan.huen@samsung.com>
Fri, 11 Aug 2017 02:54:10 +0000 (19:54 -0700)
committerKeith Busch <keith.busch@intel.com>
Thu, 17 Aug 2017 19:06:12 +0000 (15:06 -0400)
Signed-off-by: Kwan (Hingkwan) Huen-SSI <kwan.huen@samsung.com>
Signed-off-by: Keith Busch <keith.busch@intel.com>
linux/nvme.h
nvme-builtin.h
nvme-ioctl.c
nvme-ioctl.h
nvme-print.c
nvme-print.h
nvme.c

index 19354a59114bb844d71e731bc34c474a70e51274..44b8bd02373311d54154926bb5382953f9fb71de 100644 (file)
@@ -253,6 +253,7 @@ enum {
        NVME_CTRL_ONCS_WRITE_ZEROES             = 1 << 3,
        NVME_CTRL_VWC_PRESENT                   = 1 << 0,
        NVME_CTRL_OACS_SEC_SUPP                 = 1 << 0,
+       NVME_CTRL_OACS_DIRECTIVES               = 1 << 5,
        NVME_CTRL_OACS_DBBUF_SUPP               = 1 << 7,
 };
 
@@ -303,6 +304,19 @@ enum {
        NVME_ID_CNS_CTRL_LIST           = 0x13,
 };
 
+enum {
+       NVME_DIR_IDENTIFY               = 0x00,
+       NVME_DIR_STREAMS                = 0x01,
+       NVME_DIR_SND_ID_OP_ENABLE       = 0x01,
+       NVME_DIR_SND_ST_OP_REL_ID       = 0x01,
+       NVME_DIR_SND_ST_OP_REL_RSC      = 0x02,
+       NVME_DIR_RCV_ID_OP_PARAM        = 0x01,
+       NVME_DIR_RCV_ST_OP_PARAM        = 0x01,
+       NVME_DIR_RCV_ST_OP_STATUS       = 0x02,
+       NVME_DIR_RCV_ST_OP_RESOURCE     = 0x03,
+       NVME_DIR_ENDIR                  = 0x01,
+};
+
 enum {
        NVME_NS_FEAT_THIN       = 1 << 0,
        NVME_NS_FLBAS_LBA_MASK  = 0xf,
@@ -582,6 +596,7 @@ enum {
        NVME_RW_PRINFO_PRCHK_APP        = 1 << 11,
        NVME_RW_PRINFO_PRCHK_GUARD      = 1 << 12,
        NVME_RW_PRINFO_PRACT            = 1 << 13,
+       NVME_RW_DTYPE_STREAMS           = 1 << 4,
 };
 
 struct nvme_dsm_cmd {
@@ -656,6 +671,8 @@ enum nvme_admin_opcode {
        nvme_admin_download_fw          = 0x11,
        nvme_admin_ns_attach            = 0x15,
        nvme_admin_keep_alive           = 0x18,
+       nvme_admin_directive_send       = 0x19,
+       nvme_admin_directive_recv       = 0x1a,
        nvme_admin_dbbuf                = 0x7C,
        nvme_admin_format_nvm           = 0x80,
        nvme_admin_security_send        = 0x81,
@@ -983,6 +1000,23 @@ struct nvme_dbbuf {
        __u32                   rsvd12[6];
 };
 
+struct nvme_directive_cmd {
+       __u8                    opcode;
+       __u8                    flags;
+       __u16                   command_id;
+       __le32                  nsid;
+       __u64                   rsvd2[2];
+       union nvme_data_ptr     dptr;
+       __le32                  numd;
+       __u8                    doper;
+       __u8                    dtype;
+       __le16                  dspec;
+       __u8                    endir;
+       __u8                    tdtype;
+       __u16                   rsvd15;
+       __u32                   rsvd16[3];
+};
+
 struct nvme_command {
        union {
                struct nvme_common_command common;
@@ -1003,6 +1037,7 @@ struct nvme_command {
                struct nvmf_property_set_command prop_set;
                struct nvmf_property_get_command prop_get;
                struct nvme_dbbuf dbbuf;
+               struct nvme_directive_cmd directive;
        };
 };
 
index 2143ffdf7ff04577b945a0699feaf71c1d28cde3..b24c25b98d80f0639400491d2b5a14ec063eec5e 100644 (file)
@@ -52,6 +52,8 @@ COMMAND_LIST(
        ENTRY("connect", "Connect to NVMeoF subsystem", connect_cmd)
        ENTRY("disconnect", "Disconnect from NVMeoF subsystem", disconnect_cmd)
        ENTRY("gen-hostnqn", "Generate NVMeoF host NQN", gen_hostnqn_cmd)
+       ENTRY("dir-receive", "Submit a Directive Receive command, return results", dir_receive)
+       ENTRY("dir-send", "Submit a Directive Send command, return results", dir_send)
 );
 
 #endif
index e0955b7efa4c8aa8c937561db56acbf97efdc717..ca77c441169fd18949e34dc07df23dd0c4b5df92 100644 (file)
@@ -588,3 +588,44 @@ int nvme_sec_recv(int fd, __u32 nsid, __u8 nssf, __u16 spsp,
                *result = cmd.result;
        return err;
 }
+
+int nvme_dir_send(int fd, __u32 nsid, __u16 dspec, __u8 dtype, __u8 doper,
+                  __u32 data_len, __u32 dw12, void *data, __u32 *result)
+{
+        struct nvme_admin_cmd cmd = {
+                .opcode         = nvme_admin_directive_send,
+                .addr           = (__u64)(uintptr_t) data,
+                .data_len       = data_len,
+                .nsid           = nsid,
+                .cdw10          = data_len? (data_len >> 2) - 1 : 0,
+                .cdw11          = dspec << 16 | dtype << 8 | doper,
+                .cdw12          = dw12,
+        };
+        int err;
+
+        err = nvme_submit_admin_passthru(fd, &cmd);
+        if (!err && result)
+                *result = cmd.result;
+        return err;
+}
+
+int nvme_dir_recv(int fd, __u32 nsid, __u16 dspec, __u8 dtype, __u8 doper,
+                  __u32 data_len, __u32 dw12, void *data, __u32 *result)
+{
+        struct nvme_admin_cmd cmd = {
+                .opcode         = nvme_admin_directive_recv,
+                .addr           = (__u64)(uintptr_t) data,
+                .data_len       = data_len,
+                .nsid           = nsid,
+                .cdw10          = data_len? (data_len >> 2) - 1 : 0,
+                .cdw11          = dspec << 16 | dtype << 8 | doper,
+                .cdw12          = dw12,
+        };
+        int err;
+
+        err = nvme_submit_admin_passthru(fd, &cmd);
+        if (!err && result)
+                *result = cmd.result;
+        return err;
+}
+
index 78187216c09e86fc2f4dc04aa04b2ec68ed8ce2e..4aee90af9cf51cb43ba0b7d30887b0adc9469bfa 100644 (file)
@@ -116,4 +116,9 @@ int nvme_sec_recv(int fd, __u32 nsid, __u8 nssf, __u16 spsp,
 int nvme_subsystem_reset(int fd);
 int nvme_reset_controller(int fd);
 
+int nvme_dir_send(int fd, __u32 nsid, __u16 dspec, __u8 dtype, __u8 doper,
+                 __u32 data_len, __u32 dw12, void *data, __u32 *result);
+int nvme_dir_recv(int fd, __u32 nsid, __u16 dspec, __u8 dtype, __u8 doper,
+                 __u32 data_len, __u32 dw12, void *data, __u32 *result);
+
 #endif                         /* _NVME_LIB_H */
index f03382fec5a3e54c3bccab89a4c41d27791bbb5a..cec0bdb6ac094714ba06abfd4bcc938b5a2b084f 100644 (file)
@@ -122,7 +122,9 @@ static void show_nvme_id_ctrl_ctratt(__le32 ctrl_ctratt)
 static void show_nvme_id_ctrl_oacs(__le16 ctrl_oacs)
 {
        __u16 oacs = le16_to_cpu(ctrl_oacs);
-       __u16 rsvd = (oacs & 0xFFF0) >> 4;
+       __u16 rsvd = (oacs & 0xFFF0) >> 6;
+       __u16 dir = (oacs & 0x20) >> 5;
+       __u16 sft = (oacs & 0x10) >> 4;
        __u16 nsm = (oacs & 0x8) >> 3;
        __u16 fwc = (oacs & 0x4) >> 2;
        __u16 fmt = (oacs & 0x2) >> 1;
@@ -130,6 +132,10 @@ static void show_nvme_id_ctrl_oacs(__le16 ctrl_oacs)
 
        if (rsvd)
                printf(" [15:4] : %#x\tReserved\n", rsvd);
+       printf("  [5:5] : %#x\tDirectives %sSupported\n",
+               dir, dir ? "" : "Not ");
+       printf("  [4:4] : %#x\tDevice Self-test %sSupported\n",
+               sft, sft ? "" : "Not ");
        printf("  [3:3] : %#x\tNS Management and Attachment %sSupported\n",
                nsm, nsm ? "" : "Not ");
        printf("  [2:2] : %#x\tFW Commit and Download %sSupported\n",
@@ -1242,6 +1248,57 @@ static void show_host_mem_buffer(struct nvme_host_mem_buffer *hmb)
        printf("\tHost Memory Buffer Size                  (HSIZE): %u\n", hmb->hsize);
 }
 
+void nvme_directive_show_fields(__u8 dtype, __u8 doper, unsigned int result, unsigned char *buf)
+{
+        __u8 *field = buf;
+        int count, i;
+        switch (dtype) {
+        case NVME_DIR_IDENTIFY:
+                switch (doper) {
+                case NVME_DIR_RCV_ID_OP_PARAM:
+                        printf("\tDirective support \n");
+                        printf("\t\tIdentify Directive  : %s\n", (*field & 0x1) ? "supported":"not supported");
+                        printf("\t\tStream Directive    : %s\n", (*field & 0x2) ? "supported":"not supported");
+                        printf("\tDirective status \n");
+                        printf("\t\tIdentify Directive  : %s\n", (*(field + 32) & 0x1) ? "enabled" : "disabled");
+                        printf("\t\tStream Directive    : %s\n", (*(field + 32) & 0x2) ? "enabled" : "disabled");
+                        break;
+                default:
+                        fprintf(stderr, "invalid directive operations for Identify Directives\n");
+                }
+                break;
+        case NVME_DIR_STREAMS:
+                switch (doper) {
+                case NVME_DIR_RCV_ST_OP_PARAM:
+                        printf("\tMax Streams Limit                          (MSL): %u\n", *(__u16 *) field);
+                        printf("\tNVM Subsystem Streams Available           (NSSA): %u\n", *(__u16 *) (field + 2));
+                        printf("\tNVM Subsystem Streams Open                (NSSO): %u\n", *(__u16 *) (field + 4));
+                        printf("\tStream Write Size (in unit of LB size)     (SWS): %u\n", *(__u32 *) (field + 16));
+                        printf("\tStream Granularity Size (in unit of SWS)   (SGS): %u\n", *(__u16 *) (field + 20));
+                        printf("\tNamespece Streams Allocated                (NSA): %u\n", *(__u16 *) (field + 22));
+                        printf("\tNamespace Streams Open                     (NSO): %u\n", *(__u16 *) (field + 24));
+                        break;
+                case NVME_DIR_RCV_ST_OP_STATUS:
+                        count = *(__u16 *) field;
+                        printf("\tOpen Stream Count  : %u\n", *(__u16 *) field);
+                        for ( i = 0; i < count; i++ ) {
+                                printf("\tStream Identifier %.6u : %u\n", i + 1, *(__u16 *) (field + ((i + 1) * 2)));
+                        }
+                        break;
+                case NVME_DIR_RCV_ST_OP_RESOURCE:
+                        printf("\tNamespace Streams Allocated (NSA): %u\n", result & 0xffff);
+                        break;
+                default:
+                        fprintf(stderr, "invalid directive operations for Streams Directives\n");
+                }
+                break;
+        default:
+                fprintf(stderr, "invalid directive type\n");
+                break;
+        }
+        return;
+}
+
 void nvme_feature_show_fields(__u32 fid, unsigned int result, unsigned char *buf)
 {
        __u8 field;
index fd89c9631c05ab6d7ce7f8b0adc4eeb8d8deb8ec..4cdcb957ee3d3e928f7993e1b8293042ec6d8b29 100644 (file)
@@ -29,6 +29,7 @@ void show_ctrl_registers(void *bar, unsigned int mode);
 void show_nvme_id_ns_descs(void *data);
 
 void nvme_feature_show_fields(__u32 fid, unsigned int result, unsigned char *buf);
+void nvme_directive_show_fields(__u8 dtype, __u8 doper, unsigned int result, unsigned char *buf);
 char *nvme_status_to_string(__u32 status);
 char *nvme_select_to_string(int sel);
 char *nvme_feature_to_string(int feature);
diff --git a/nvme.c b/nvme.c
index 979fa15befca879b5312a89320f82c11d5206549..d6544d92a3085ce1eeb67123f0f03be044b6131e 100644 (file)
--- a/nvme.c
+++ b/nvme.c
@@ -1925,6 +1925,142 @@ static int sec_send(int argc, char **argv, struct command *cmd, struct plugin *p
        return err;
 }
 
+static int dir_send(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+        const char *desc = "Set directive parameters of the "\
+                "specified directive type.";
+        const char *raw_binary = "show infos in binary format";
+        const char *namespace_id = "identifier of desired namespace";
+        const char *data_len = "buffer len (if) data is returned";
+        const char *dtype = "directive type";
+        const char *dspec = "directive specification associated with directive type";
+        const char *doper = "directive operation";
+        const char *endir = "directive enable";
+        const char *ttype = "target directive type to be enabled/disabled";
+        const char *human_readable = "show infos in readable format";
+        int err, fd;
+        __u32 result;
+        __u32 dw12 = 0;
+        void *buf = NULL;
+        int ffd = STDIN_FILENO;
+
+        struct config {
+                char *file;
+                __u32 namespace_id;
+                __u32 data_len;
+                __u16 dspec;
+                __u8  dtype;
+                __u8  doper;
+                __u16 endir;
+                __u8  ttype;
+                int  raw_binary;
+                int  human_readable;
+        };
+
+        struct config cfg = {
+                .file         = "",
+                .namespace_id = 1,
+                .data_len     = 0,
+                .dspec        = 0,
+                .dtype        = 0,
+                .ttype        = 0,
+                .doper        = 0,
+                .endir        = 1,
+        };
+
+        const struct argconfig_commandline_options command_line_options[] = {
+                {"namespace-id",   'n', "NUM", CFG_POSITIVE, &cfg.namespace_id,   required_argument, namespace_id},
+                {"data-len",       'l', "NUM", CFG_POSITIVE, &cfg.data_len,       required_argument, data_len},
+                {"raw-binary",     'b', "FLAG",CFG_NONE,     &cfg.raw_binary,     no_argument,       raw_binary},
+                {"dir-type",       'D', "NUM", CFG_BYTE,     &cfg.dtype,          required_argument, dtype},
+                {"target-dir",     'T', "NUM", CFG_BYTE,     &cfg.ttype,          required_argument, ttype},
+                {"dir-spec",       'S', "NUM", CFG_SHORT,    &cfg.dspec,          required_argument, dspec},
+                {"dir-oper",       'O', "NUM", CFG_BYTE,     &cfg.doper,          required_argument, doper},
+                {"endir",          'e', "NUM", CFG_SHORT,    &cfg.endir,          required_argument, endir},
+                {"human-readable", 'H', "FLAG",CFG_NONE,     &cfg.human_readable, no_argument,       human_readable},
+                {NULL}
+        };
+
+        fd = parse_and_open(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
+       if (fd < 0)
+               return fd;
+
+        switch (cfg.dtype) {
+        case NVME_DIR_IDENTIFY:
+                switch (cfg.doper) {
+                case NVME_DIR_SND_ID_OP_ENABLE:
+                        if (!cfg.ttype) {
+                                fprintf(stderr, "target-dir required param\n");
+                                return EINVAL;
+                        }
+                        dw12 = cfg.ttype << 8 | cfg.endir;
+                        break;
+                default:
+                        fprintf(stderr, "invalid directive operations for Identify Directives\n");
+                        return EINVAL;
+                }
+                break;
+        case NVME_DIR_STREAMS:
+                switch (cfg.doper) {
+                case NVME_DIR_SND_ST_OP_REL_ID:
+                case NVME_DIR_SND_ST_OP_REL_RSC:
+                        break;
+                default:
+                        fprintf(stderr, "invalid directive operations for Streams Directives\n");
+                        return EINVAL;
+                }
+                break;
+        default:
+                fprintf(stderr, "invalid directive type\n");
+                return EINVAL;
+                break;
+        }
+
+
+        if (cfg.data_len) {
+                if (posix_memalign(&buf, getpagesize(), cfg.data_len))
+                        exit(ENOMEM);
+                memset(buf, 0, cfg.data_len);
+        }
+
+        if (buf) {
+                if (strlen(cfg.file)) {
+                        ffd = open(cfg.file, O_RDONLY);
+                        if (ffd <= 0) {
+                                fprintf(stderr, "no firmware file provided\n");
+                                return -EINVAL;
+                        }
+                }
+                if (read(ffd, (void *)buf, cfg.data_len) < 0) {
+                        fprintf(stderr, "failed to read data buffer from input file\n");
+                        return EINVAL;
+                }
+        }
+
+        err = nvme_dir_send(fd, cfg.namespace_id, cfg.dspec, cfg.dtype, cfg.doper,
+                                cfg.data_len, dw12, buf, &result);
+        if (err < 0) {
+                perror("dir-send");
+                return errno;
+        }
+        if (!err) {
+                printf("dir-send: type %#x, operation %#x, spec_val %#x, nsid %#x, result %#x \n",
+                                cfg.dtype, cfg.doper, cfg.dspec, cfg.namespace_id, result);
+                if (buf) {
+                        if (!cfg.raw_binary)
+                                d(buf, cfg.data_len, 16, 1);
+                        else
+                                d_raw(buf, cfg.data_len);
+                }
+        }
+        else if (err > 0)
+                fprintf(stderr, "NVMe Status:%s(%x)\n",
+                                nvme_status_to_string(err), err);
+        if (buf)
+                free(buf);
+        return err;
+}
+
 static int write_uncor(int argc, char **argv, struct command *cmd, struct plugin *plugin)
 {
        int err, fd;
@@ -2753,6 +2889,130 @@ static int sec_recv(int argc, char **argv, struct command *cmd, struct plugin *p
        return err;
 }
 
+static int dir_receive(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+        const char *desc = "Read directive parameters of the "\
+                "specified directive type.";
+        const char *raw_binary = "show infos in binary format";
+        const char *namespace_id = "identifier of desired namespace";
+        const char *data_len = "buffer len (if) data is returned";
+        const char *dtype = "directive type";
+        const char *dspec = "directive specification associated with directive type";
+        const char *doper = "directive operation";
+        const char *nsr = "namespace stream requested";
+        const char *human_readable = "show infos in readable format";
+        int err, fd;
+        __u32 result;
+        __u32 dw12 = 0;
+        void *buf = NULL;
+
+        struct config {
+                __u32 namespace_id;
+                __u32 data_len;
+                __u16 dspec;
+                __u8  dtype;
+                __u8  doper;
+                __u16 nsr; /* dw12 for NVME_DIR_ST_RCVOP_STATUS */
+                int  raw_binary;
+                int  human_readable;
+        };
+
+        struct config cfg = {
+                .namespace_id = 1,
+                .data_len     = 0,
+                .dspec        = 0,
+                .dtype        = 0,
+                .doper        = 0,
+                .nsr          = 0,
+        };
+
+        const struct argconfig_commandline_options command_line_options[] = {
+                {"namespace-id",   'n', "NUM", CFG_POSITIVE, &cfg.namespace_id,   required_argument, namespace_id},
+                {"data-len",       'l', "NUM", CFG_POSITIVE, &cfg.data_len,       required_argument, data_len},
+                {"raw-binary",     'b', "FLAG",CFG_NONE,     &cfg.raw_binary,     no_argument,       raw_binary},
+                {"dir-type",       'D', "NUM", CFG_BYTE,     &cfg.dtype,          required_argument, dtype},
+                {"dir-spec",       'S', "NUM", CFG_SHORT,    &cfg.dspec,          required_argument, dspec},
+                {"dir-oper",       'O', "NUM", CFG_BYTE,     &cfg.doper,          required_argument, doper},
+                {"req-resource",   'r', "NUM", CFG_SHORT,    &cfg.nsr,            required_argument, nsr},
+                {"human-readable", 'H', "FLAG",CFG_NONE,     &cfg.human_readable, no_argument,       human_readable},
+                {NULL}
+        };
+
+        fd = parse_and_open(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
+       if (fd < 0)
+               return fd;
+
+        switch (cfg.dtype) {
+        case NVME_DIR_IDENTIFY:
+                switch (cfg.doper) {
+                case NVME_DIR_RCV_ID_OP_PARAM:
+                        if (!cfg.data_len)
+                                cfg.data_len = 4096;
+                        break;
+                default:
+                        fprintf(stderr, "invalid directive operations for Identify Directives\n");
+                        return EINVAL;
+                }
+                break;
+        case NVME_DIR_STREAMS:
+                switch (cfg.doper) {
+                case NVME_DIR_RCV_ST_OP_PARAM:
+                        if (!cfg.data_len)
+                                cfg.data_len = 32;
+                        break;
+                case NVME_DIR_RCV_ST_OP_STATUS:
+                        if (!cfg.data_len)
+                                cfg.data_len = 128 * 1024;
+                        break;
+                case NVME_DIR_RCV_ST_OP_RESOURCE:
+                        dw12 = cfg.nsr;
+                        break;
+                default:
+                        fprintf(stderr, "invalid directive operations for Streams Directives\n");
+                        return EINVAL;
+                }
+                break;
+        default:
+                fprintf(stderr, "invalid directive type\n");
+                return EINVAL;
+                break;
+        }
+
+        if (cfg.data_len) {
+                if (posix_memalign(&buf, getpagesize(), cfg.data_len))
+                        exit(ENOMEM);
+                memset(buf, 0, cfg.data_len);
+        }
+
+        err = nvme_dir_recv(fd, cfg.namespace_id, cfg.dspec, cfg.dtype, cfg.doper,
+                        cfg.data_len, dw12, buf, &result);
+        if (err < 0) {
+                perror("dir-receive");
+                return errno;
+        }
+
+        if (!err) {
+                printf("dir-receive: type %#x, operation %#x, spec %#x, nsid %#x, result %#x \n",
+                                cfg.dtype, cfg.doper, cfg.dspec, cfg.namespace_id, result);
+                if (cfg.human_readable)
+                        nvme_directive_show_fields(cfg.dtype, cfg.doper, result, buf);
+                else {
+                        if (buf) {
+                                if (!cfg.raw_binary)
+                                        d(buf, cfg.data_len, 16, 1);
+                                else
+                                        d_raw(buf, cfg.data_len);
+                        }
+                }
+        }
+        else if (err > 0)
+                fprintf(stderr, "NVMe Status:%s(%x)\n",
+                                nvme_status_to_string(err), err);
+        if (buf)
+                free(buf);
+        return err;
+}
+
 static int passthru(int argc, char **argv, int ioctl_cmd, const char *desc, struct command *cmd)
 {
        void *data = NULL, *metadata = NULL;