]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
Implement Intel Vendor specific logs
authorScott Bauer <scott.bauer@intel.com>
Fri, 16 Jun 2017 19:19:04 +0000 (13:19 -0600)
committerScott Bauer <scott.bauer@intel.com>
Mon, 26 Jun 2017 19:12:13 +0000 (13:12 -0600)
This adds support for pulling Assert/Event/Nlogs on
All current Intel drives including 900p/P4600/P3700.

Signed-off-by: Scott Bauer <scott.bauer@intel.com>
intel-nvme.c

index b650bd7c77ebeb4ac9995d3ae1a1212ecd85ff8c..8d777ffe2d58e87d588db57aadd474c82167b322 100644 (file)
@@ -428,112 +428,367 @@ static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct
        return err;
 }
 
+struct intel_assert_dump {
+    __u32 coreoffset;
+    __u32 assertsize;
+    __u8  assertdumptype;
+    __u8  assertvalid;
+    __u8  reserved[2];
+};
+
+struct intel_event_dump {
+       __u32 numeventdumps;
+       __u32 coresize;
+       __u32 coreoffset;
+       __u32 eventidoffset[16];
+       __u8  eventIdValidity[16];
+};
+
+struct intel_vu_version {
+       __u16    major;
+       __u16    minor;
+};
+
+struct intel_event_header {
+       __u32 eventidsize;
+       struct intel_event_dump edumps[0];
+};
+
 struct intel_vu_log {
-    __u16    major;
-    __u16    minor;
+    struct intel_vu_version ver;
     __u32    header;
     __u32    size;
-    __u8     reserved[4084];
+    __u32    numcores;
+    __u8     reserved[4080];
+};
+
+struct intel_vu_nlog {
+       struct intel_vu_version ver;
+       __u32 logselect;
+       __u32 totalnlogs;
+       __u32 nlognum;
+       __u32 nlogname;
+       __u32 nlogbytesize;
+       __u32 nlogprimarybuffsize;
+       __u32 tickspersecond;
+       __u32 corecount;
+       __u32 nlogpausestatus;
+       __u32 selectoffsetref;
+       __u32 selectnlogpause;
+       __u32 selectaddedoffset;
+       __u32 nlogbufnum;
+       __u32 nlogbufnummax;
+       __u32 coreselected;
+       __u32 reserved[3];
+};
+
+struct intel_cd_log {
+    union {
+       struct {
+               __u32 selectLog  : 3;
+               __u32 selectCore : 2;
+               __u32 selectNlog : 8;
+               __u8  selectOffsetRef : 1;
+               __u32 selectNlogPause : 2;
+               __u32 reserved2  : 16;
+       }fields;
+       __u32 entireDword;
+    }u;
 };
 
+#define max(x,y) (x) > (y) ? (x) : (y)
+#define min(x,y) (x) > (y) ? (y) : (x)
+
+static void print_intel_nlog(struct intel_vu_nlog *intel_nlog)
+{
+       printf("Version Major %u\n"
+              "Version Minor %u\n"
+              "Log_select %u\n"
+              "totalnlogs %u\n"
+              "nlognum %u\n"
+              "nlogname %u\n"
+              "nlogbytesze %u\n"
+              "nlogprimarybuffsize %u\n"
+              "tickspersecond %u\n"
+              "corecount %u\n"
+              "nlogpausestatus %u\n"
+              "selectoffsetref %u\n"
+              "selectnlogpause %u\n"
+              "selectaddedoffset %u\n"
+              "nlogbufnum %u\n"
+              "nlogbufnummax %u\n"
+              "coreselected %u\n",
+              intel_nlog->ver.major, intel_nlog->ver.minor,
+              intel_nlog->logselect, intel_nlog->totalnlogs, intel_nlog->nlognum,
+              intel_nlog->nlogname, intel_nlog->nlogbytesize,
+              intel_nlog->nlogprimarybuffsize, intel_nlog->tickspersecond,
+              intel_nlog->corecount, intel_nlog->nlogpausestatus,
+              intel_nlog->selectoffsetref, intel_nlog->selectnlogpause,
+              intel_nlog->selectaddedoffset, intel_nlog->nlogbufnum,
+              intel_nlog->nlogbufnummax, intel_nlog->coreselected);
+}
+
+static int read_entire_cmd(struct nvme_passthru_cmd *cmd, int total_size,
+                          const size_t max_tfer, int out_fd, int ioctl_fd,
+                          __u8 *buf)
+{
+       int err = 0;
+       size_t dword_tfer = 0;
+
+       dword_tfer = min(max_tfer, total_size);
+       while (total_size > 0) {
+               err = nvme_submit_passthru(ioctl_fd, NVME_IOCTL_ADMIN_CMD, cmd);
+               if (err) {
+                       printf("failed on cmd.data_len %u cmd.cdw13 %u cmd.cdw12 %x cmd.cdw10 %u err %x remaining size %d\n", cmd->data_len, cmd->cdw13, cmd->cdw12, cmd->cdw10, err, total_size);
+                       goto out;
+               }
+
+               if (out_fd > 0) {
+                       err = write(out_fd, buf, cmd->data_len);
+                       if (err < 0) {
+                               perror("write failure");
+                               goto out;
+                       }
+                       err = 0;
+               }
+               total_size -= dword_tfer;
+               cmd->cdw13 += dword_tfer;
+               cmd->cdw10 = dword_tfer = min(max_tfer, total_size);
+               cmd->data_len = (min(max_tfer, total_size)) * 4;
+       }
+
+ out:
+       return err;
+}
+
+static int write_header(__u8 *buf, int fd, size_t amnt)
+{
+       if (write(fd, buf, amnt) < 0)
+               return 1;
+       return 0;
+}
+
+static int read_header(struct nvme_passthru_cmd *cmd,__u8 *buf, int ioctl_fd, __u32 dw12, int nsid)
+{
+       memset(cmd, 0, sizeof(*cmd));
+       memset(buf, 0, 4096);
+       cmd->opcode = 0xd2;
+       cmd->nsid = nsid;
+       cmd->cdw10 = 0x400;
+       cmd->cdw12 = dw12;
+       cmd->data_len = 0x1000;
+       cmd->addr = (unsigned long)(void *)buf;
+       return read_entire_cmd(cmd, 0x400, 0x400, -1, ioctl_fd, buf);
+}
+
+static int setup_file(char *f, char *file, int fd, int type)
+{
+       struct nvme_id_ctrl ctrl;
+       int err = 0, i = sizeof(ctrl.sn) - 1;
+
+       err = nvme_identify_ctrl(fd, &ctrl);
+       if (err)
+               return err;
+
+       /* Remove trailing spaces from the name */
+       while (i && ctrl.sn[i] == ' ') {
+               ctrl.sn[i] = '\0';
+               i--;
+       }
+
+       sprintf(f, "%s_%-.*s.bin", type == 0 ? "Nlog" :
+               type == 1 ? "EventLog" :  "AssertLog",
+               (int)sizeof(ctrl.sn), ctrl.sn);
+       return err;
+}
+
+static int get_internal_log_old(__u8 *buf, int output, int fd,
+                               struct nvme_passthru_cmd *cmd)
+{
+       struct intel_vu_log *intel;
+       int err = 0;
+       const int dwmax = 0x400;
+       const int dmamax = 0x1000;
+
+       intel = (struct intel_vu_log *)buf;
+
+       printf("Log major:%d minor:%d header:%d size:%d\n",
+               intel->ver.major, intel->ver.minor, intel->header, intel->size);
+
+       err = write(output, buf, 0x1000);
+       if (err < 0) {
+               perror("write failure");
+               goto out;
+       }
+       intel->size -= 0x400;
+       cmd->opcode = 0xd2;
+       cmd->cdw10 = min(dwmax, intel->size);
+       cmd->data_len = min(dmamax, intel->size);
+       err = read_entire_cmd(cmd, intel->size, dwmax, output, fd, buf);
+       if (err)
+               goto out;
+
+       err = 0;
+ out:
+       return err;
+}
+
 static int get_internal_log(int argc, char **argv, struct command *command, struct plugin *plugin)
 {
-       __u8 buf[0x1000];
+       __u8 buf[0x2000];
        char f[0x100];
-       int err, fd, output, size;
+       int err, fd, output, i, j, count = 0, core_num = 1;//, remainder;
        struct nvme_passthru_cmd cmd;
-       struct intel_vu_log *intel;
-       struct nvme_id_ctrl ctrl;
+       static struct intel_vu_log intel;
+       struct intel_assert_dump *ad;
+       struct intel_vu_nlog *intel_nlog;
+       struct intel_event_header *ehdr;
+
 
        char *desc = "Get Intel Firmware Log and save it.";
        char *log = "Log type: 0, 1, or 2 for nlog, event log, and assert log, respectively.";
+       char *core = "Select which region log should come from. -1 for all";
+       char *nlognum = "Select which nlog to read. -1 for all nlogs";
        char *file = "Output file; defaults to device name provided";
+       char *verb = "To print out verbose nlog info";
        const char *namespace_id = "Namespace to get logs from";
 
        struct config {
                __u32 namespace_id;
                __u32 log;
+               int core;
+               int lnum;
                char *file;
+               bool verbose;
        };
 
        struct config cfg = {
-               .namespace_id = 0,
+               .namespace_id = -1,
                .file = NULL
        };
 
+       struct intel_cd_log cdlog;
+
        const struct argconfig_commandline_options command_line_options[] = {
                {"log",          'l', "NUM",  CFG_POSITIVE, &cfg.log,          required_argument, log},
+               {"region",       'r', "NUM",  CFG_INT, &cfg.core,         required_argument, core},
+               {"nlognum",      'm', "NUM",  CFG_INT, &cfg.lnum,         required_argument, nlognum},
                {"namespace-id", 'n', "NUM",  CFG_POSITIVE, &cfg.namespace_id, required_argument, namespace_id},
                {"output-file",  'o', "FILE", CFG_STRING,   &cfg.file,         required_argument, file},
+               {"verbose_nlog", 'v', ""    , CFG_NONE,     &cfg.verbose,     no_argument, verb},
                {NULL}
        };
 
        fd = parse_and_open(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
-       if (cfg.log > 2)
+       if (cfg.log > 2 || cfg.core > 4 || cfg.lnum > 255)
                return EINVAL;
 
        if (!cfg.file) {
-               int i = sizeof(ctrl.sn) - 1;
-
-               err = nvme_identify_ctrl(fd, &ctrl);
+               err = setup_file(f, cfg.file, fd, cfg.log);
                if (err)
                        goto out;
-
-               /* Remove trailing spaces from the name */
-               while (i && ctrl.sn[i] == ' ') {
-                       ctrl.sn[i] = '\0';
-                       i--;
-               }
-
-               sprintf(f, "%s_%-.*s.bin", cfg.log == 0 ? "Nlog" :
-                               cfg.log == 2 ? "EventLog" :  "AssertLog",
-                               (int)sizeof(ctrl.sn), ctrl.sn);
                cfg.file = f;
        }
 
-       output = open(cfg.file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+       memset(&cdlog, 0, sizeof(cdlog));
+       memset(&intel_nlog, 0, sizeof(intel_nlog));
 
-       memset(&cmd, 0, sizeof(cmd));
-       cmd.opcode = 0xd2;
-       cmd.nsid = cfg.namespace_id;
-       cmd.cdw10 = 0x400;
-       cmd.cdw12 = cfg.log;
-       cmd.data_len = 0x1000;
-       cmd.addr = (unsigned long)(void *)buf;
+       cdlog.u.fields.selectLog = cfg.log;
+       cdlog.u.fields.selectCore = cfg.core < 0 ? 0 : cfg.core;
+       cdlog.u.fields.selectNlog = cfg.lnum < 0 ? 0 : cfg.lnum;
 
-       memset(buf, 0, sizeof(buf));
-       err = nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD, &cmd);
+       output = open(cfg.file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+
+       err = read_header(&cmd, buf, fd, cdlog.u.entireDword, cfg.namespace_id);
        if (err)
                goto out;
 
-       intel = (struct intel_vu_log *)buf;
-       size = intel->size * 4; /* size reported in dwords */
-
-       printf("Log major:%d minor:%d header:%d size:%d\n",
-               intel->major, intel->minor, intel->header, intel->size);
+       memcpy(&intel, buf, sizeof(intel));
+       cmd.addr = (unsigned long)(void *)buf;
+       core_num = 1;
 
-       err = write(output, buf, 0x1000);
-       if (err < 0) {
-               perror("write failure");
+       /* for 1.1 Fultondales will use old nlog, but current assert/event */
+       if ((intel.ver.major < 1 && intel.ver.minor < 1) ||
+           (intel.ver.major <= 1 && intel.ver.minor <= 1 && cfg.log == 0)) {
+               err = get_internal_log_old(buf, output, fd, &cmd);
                goto out;
        }
-       size -= 0x1000;
 
-       while (size > 0) {
-               cmd.cdw13 += 0x400;
-               err = nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD, &cmd);
-               if (err)
-                       goto out;
+       if (cfg.log == 2) {
+               if (cfg.verbose)
+                       printf("Log major:%d minor:%d header:%d size:%d numcores:%d\n",
+                              intel.ver.major, intel.ver.minor, intel.header, intel.size,
+                              intel.numcores);
 
-               err = write(output, buf, 0x1000);
-               if (err < 0) {
+               err = write_header(buf, output, 0x1000);
+               if (err) {
                        perror("write failure");
                        goto out;
                }
-               size -= 0x1000;
+
+               count = intel.numcores;
+               ad = (void *) intel.reserved;
+       } else if (!cfg.log) {
+               intel_nlog = (void*)buf;
+               if (cfg.lnum < 0)
+                       count = intel_nlog->totalnlogs;
+               else
+                       count = 1;
+               if (cfg.core < 0)
+                       core_num = intel_nlog->corecount;
+       } else if (cfg.log == 1) {
+               ehdr = (void *) intel.reserved;
+               core_num = intel.numcores;
+               count = 1;
+               err = write_header(buf, output, sizeof(intel));
+               if (err)
+                       goto out;
+       }
+
+       for (j = 0; j < core_num; j++) {
+               cdlog.u.fields.selectCore = j;
+               for (i = 0; i < count; i++) {
+                       if (cfg.log == 0 && ad[i].assertvalid) {
+                               cmd.cdw13 = ad[i].coreoffset;
+                               cmd.cdw10 = 0x400;
+                               cmd.data_len = min(0x400, ad[i].assertsize) * 4;
+                               err = read_entire_cmd(&cmd, ad[i].assertsize,
+                                                     0x400, output, fd, buf);
+                               if (err)
+                                       goto out;
+
+                       } else if(!cfg.log) {
+                               /* If the user selected to read the entire nlog */
+                               if (count > 1)
+                                       cdlog.u.fields.selectNlog = i;
+
+                               err = read_header(&cmd, buf, fd, cdlog.u.entireDword, cfg.namespace_id);
+                               if (err)
+                                       goto out;
+                               err = write_header(buf, output, sizeof(*intel_nlog));
+                               if (err)
+                                       goto out;
+                               if (cfg.verbose)
+                                       print_intel_nlog(intel_nlog);
+                               cmd.cdw13 = 0x400;
+                               cmd.cdw10 = 0x400;
+                               cmd.data_len = min(0x1000, intel_nlog->nlogbytesize);
+                               err = read_entire_cmd(&cmd, intel_nlog->nlogbytesize/4,
+                                                     0x400, output, fd, buf);
+                               if (err)
+                                       goto out;
+                       } else if (cfg.log == 1) {
+                               cmd.cdw13 = ehdr->edumps[j].coreoffset;
+                               cmd.cdw10 = 0x400;
+                               cmd.data_len = 0x400;
+                               err = read_entire_cmd(&cmd, ehdr->edumps[j].coresize,
+                                                     0x400, output, fd, buf);
+                               if (err)
+                                       goto out;
+                       }
+               }
        }
        err = 0;
-       printf("Successfully wrote log to %s\n", cfg.file);
  out:
        if (err > 0) {
                fprintf(stderr, "NVMe Status:%s(%x)\n",
@@ -541,6 +796,8 @@ static int get_internal_log(int argc, char **argv, struct command *command, stru
        } else if (err < 0) {
                perror("intel log");
                err = EIO;
-       }
+       } else
+               printf("Successfully wrote log to %s\n", cfg.file);
        return err;
+
 }