From 3d52c76a74a5fe894d9fbfcdfc9c713958f5717f Mon Sep 17 00:00:00 2001 From: Andrew Meir Date: Wed, 28 Mar 2018 17:42:53 +0200 Subject: [PATCH] Add toshiba plugin code and command documentation. --- ...me-toshiba-clear-pcie-correctable-errors.1 | 66 ++ ...toshiba-clear-pcie-correctable-errors.html | 797 +++++++++++++++++ ...-toshiba-clear-pcie-correctable-errors.txt | 34 + Documentation/nvme-toshiba-vs-internal-log.1 | 108 +++ .../nvme-toshiba-vs-internal-log.html | 843 +++++++++++++++++ .../nvme-toshiba-vs-internal-log.txt | 64 ++ Documentation/nvme-toshiba-vs-smart-add-log.1 | 106 +++ .../nvme-toshiba-vs-smart-add-log.html | 846 ++++++++++++++++++ .../nvme-toshiba-vs-smart-add-log.txt | 64 ++ toshiba-nvme.c | 639 +++++++++++++ toshiba-nvme.h | 20 + 11 files changed, 3587 insertions(+) create mode 100644 Documentation/nvme-toshiba-clear-pcie-correctable-errors.1 create mode 100644 Documentation/nvme-toshiba-clear-pcie-correctable-errors.html create mode 100644 Documentation/nvme-toshiba-clear-pcie-correctable-errors.txt create mode 100644 Documentation/nvme-toshiba-vs-internal-log.1 create mode 100644 Documentation/nvme-toshiba-vs-internal-log.html create mode 100644 Documentation/nvme-toshiba-vs-internal-log.txt create mode 100644 Documentation/nvme-toshiba-vs-smart-add-log.1 create mode 100644 Documentation/nvme-toshiba-vs-smart-add-log.html create mode 100644 Documentation/nvme-toshiba-vs-smart-add-log.txt create mode 100644 toshiba-nvme.c create mode 100644 toshiba-nvme.h diff --git a/Documentation/nvme-toshiba-clear-pcie-correctable-errors.1 b/Documentation/nvme-toshiba-clear-pcie-correctable-errors.1 new file mode 100644 index 0000000..dbe4f4e --- /dev/null +++ b/Documentation/nvme-toshiba-clear-pcie-correctable-errors.1 @@ -0,0 +1,66 @@ +'\" t +.\" Title: nvme-toshiba-clear-pcie-correctable-errors +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 03/07/2018 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-TOSHIBA\-CLEAR" "1" "03/07/2018" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-toshiba-clear-pcie-correctable-errors \- Reset the PCIe correctable errors count to zero\&. +.SH "SYNOPSIS" +.sp +.nf +\*(Aqnvme toshiba clear\-pcie\-correctable\-errors \*(Aq +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, sends the Toshiba clear PCIe correctable errors request\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Clear the PCIe correctable errors count: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme toshiba clear\-pcie\-correctable\-errors /dev/nvme0 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite diff --git a/Documentation/nvme-toshiba-clear-pcie-correctable-errors.html b/Documentation/nvme-toshiba-clear-pcie-correctable-errors.html new file mode 100644 index 0000000..0609f15 --- /dev/null +++ b/Documentation/nvme-toshiba-clear-pcie-correctable-errors.html @@ -0,0 +1,797 @@ + + + + + +nvme-toshiba-clear-pcie-correctable-errors(1) + + + + + +
+
+

SYNOPSIS

+
+
+
'nvme toshiba clear-pcie-correctable-errors ' <device>
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, sends the Toshiba clear PCIe correctable errors +request.

+

The <device> parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1).

+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Clear the PCIe correctable errors count: +

    +
    +
    +
    # nvme toshiba clear-pcie-correctable-errors /dev/nvme0
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-toshiba-clear-pcie-correctable-errors.txt b/Documentation/nvme-toshiba-clear-pcie-correctable-errors.txt new file mode 100644 index 0000000..5871900 --- /dev/null +++ b/Documentation/nvme-toshiba-clear-pcie-correctable-errors.txt @@ -0,0 +1,34 @@ +nvme-toshiba-clear-pcie-correctable-errors(1) +============================================= + +NAME +---- +nvme-toshiba-clear-pcie-correctable-errors - Reset the PCIe correctable errors count to zero. + +SYNOPSIS +-------- +[verse] +'nvme toshiba clear-pcie-correctable-errors ' + + +DESCRIPTION +----------- +For the NVMe device given, sends the Toshiba clear PCIe correctable errors +request. + +The parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1). + + +EXAMPLES +-------- +* Clear the PCIe correctable errors count: ++ +------------ +# nvme toshiba clear-pcie-correctable-errors /dev/nvme0 +------------ + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-toshiba-vs-internal-log.1 b/Documentation/nvme-toshiba-vs-internal-log.1 new file mode 100644 index 0000000..25e3c11 --- /dev/null +++ b/Documentation/nvme-toshiba-vs-internal-log.1 @@ -0,0 +1,108 @@ +'\" t +.\" Title: nvme-toshiba-vs-internal-log +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 03/07/2018 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-TOSHIBA\-VS\-I" "1" "03/07/2018" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-toshiba-vs-internal-log \- Retrieve a Toshiba device\*(Aqs vendor specific internal log and either save to file or dump the contents\&. +.SH "SYNOPSIS" +.sp +.nf +\*(Aqnvme toshiba vs\-internal\-log \*(Aq + [\-\-output\-file=, \-o ] (optional) + [\-\-saved\-log, \-s] (optional) +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, sends the Toshiba internal device log request and either saves the result to a file or dumps the content to stdout\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +The log is associated with the controller rather than any namespaces\&. +.sp +Two logs exist, the current log and the previous log\&. +.sp +This will only work on Toshiba devices supporting this feature\&. +.sp +Note: The logs are quite large \- typically 100\(cqs of MB\&. This command can take several minutes to complete\&. A progress runner is included when data is written to file and a page count is included in the stdout dump\&. +.SH "OPTIONS" +.PP +\-o , \-\-output\-file= +.RS 4 +Output binary file\&. Defaults to text\-formatted dump to stdout +.RE +.PP +\-p, \-\-prev\-log +.RS 4 +Use previous log contents\&. Defaults to the current log contents\&. +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Get the current log from the device and dump it to stdout: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme toshiba internal\-log /dev/nvme0 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Get the previous log from the device and save to a binary file: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme toshiba internal\-log /dev/nvme0 \-\-output\-file=log\&.bin \-\-prev\-log +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite diff --git a/Documentation/nvme-toshiba-vs-internal-log.html b/Documentation/nvme-toshiba-vs-internal-log.html new file mode 100644 index 0000000..3a19cb5 --- /dev/null +++ b/Documentation/nvme-toshiba-vs-internal-log.html @@ -0,0 +1,843 @@ + + + + + +nvme-toshiba-vs-internal-log(1) + + + + + +
+
+

SYNOPSIS

+
+
+
'nvme toshiba vs-internal-log ' <device>
+                [--output-file=<FILE>, -o <FILE>] (optional)
+                [--saved-log, -s] (optional)
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, sends the Toshiba internal device log +request and either saves the result to a file or dumps the content to stdout.

+

The <device> parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1).

+

The log is associated with the controller rather than any +namespaces.

+

Two logs exist, the current log and the previous log.

+

This will only work on Toshiba devices supporting this feature.

+

Note: The logs are quite large - typically 100’s of MB. This command can take several minutes to complete. +A progress runner is included when data is written to file and a page count is included in the stdout dump.

+
+
+
+

OPTIONS

+
+
+
+-o <FILE> +
+
+--output-file=<FILE> +
+
+

+ Output binary file. Defaults to text-formatted dump to stdout +

+
+
+-p +
+
+--prev-log +
+
+

+ Use previous log contents. Defaults to the current log contents. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Get the current log from the device and dump it to stdout: +

    +
    +
    +
    # nvme toshiba internal-log /dev/nvme0
    +
    +
  • +
  • +

    +Get the previous log from the device and save to a binary file: +

    +
    +
    +
    # nvme toshiba internal-log /dev/nvme0 --output-file=log.bin --prev-log
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-toshiba-vs-internal-log.txt b/Documentation/nvme-toshiba-vs-internal-log.txt new file mode 100644 index 0000000..d3c0104 --- /dev/null +++ b/Documentation/nvme-toshiba-vs-internal-log.txt @@ -0,0 +1,64 @@ +nvme-toshiba-vs-internal-log(1) +=============================== + +NAME +---- +nvme-toshiba-vs-internal-log - Retrieve a Toshiba device's vendor specific internal log and either save to file or dump the contents. + +SYNOPSIS +-------- +[verse] +'nvme toshiba vs-internal-log ' + [--output-file=, -o ] (optional) + [--saved-log, -s] (optional) + +DESCRIPTION +----------- +For the NVMe device given, sends the Toshiba internal device log +request and either saves the result to a file or dumps the content to stdout. + +The parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1). + +The log is associated with the controller rather than any +namespaces. + +Two logs exist, the current log and the previous log. + +This will only work on Toshiba devices supporting this feature. + +Note: The logs are quite large - typically 100's of MB. This command can take several minutes to complete. +A progress runner is included when data is written to file and a page count is included in the stdout dump. + +OPTIONS +------- + + +-o :: +--output-file=:: + Output binary file. Defaults to text-formatted dump to stdout + + +-p:: +--prev-log:: + Use previous log contents. Defaults to the current log contents. + +EXAMPLES +-------- +* Get the current log from the device and dump it to stdout: ++ +------------ +# nvme toshiba internal-log /dev/nvme0 +------------ ++ + +* Get the previous log from the device and save to a binary file: ++ +------------ +# nvme toshiba internal-log /dev/nvme0 --output-file=log.bin --prev-log +------------ + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-toshiba-vs-smart-add-log.1 b/Documentation/nvme-toshiba-vs-smart-add-log.1 new file mode 100644 index 0000000..82fe4b2 --- /dev/null +++ b/Documentation/nvme-toshiba-vs-smart-add-log.1 @@ -0,0 +1,106 @@ +'\" t +.\" Title: nvme-toshiba-vs-smart-add-log +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 03/07/2018 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-TOSHIBA\-VS\-S" "1" "03/07/2018" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-toshiba-vs-smart-add-log \- Retrieve a Toshiba device\*(Aqs vendor specific extended SMART log page contents and either save to file or dump the contents\&. +.SH "SYNOPSIS" +.sp +.nf +\*(Aqnvme toshiba vs\-smart\-add\-log \*(Aq [\-\-log=, \-l ] + [\-\-namespace\-id=, \-n ] + [\-\-output\-file=, \-o ] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, sends the Toshiba vendor log request and either saves the result to a file or dumps the content to stdout\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +The log contents may be associated with the controller, in which case the namespace parameter is ignored\&. +.sp +Two logs exist, page 0xC0 (log page directory) and page 0xCA (vendor log page) +.sp +This will only work on Toshiba devices supporting this feature\&. +.SH "OPTIONS" +.PP +\-l , \-\-log= +.RS 4 +Log page: 0xC0 or 0xCA (defaults to 0xCA) +.RE +.PP +\-n , \-\-namespace\-id=, \-o , \-\-output\-file= +.RS 4 +Output binary file\&. Defaults to text\-formatted dump to stdout +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Get the current log from the device and dumps it to stdout: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme toshiba vs\-smart\-add\-log /dev/nvme0 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Get the contents of log page 0xC0 from the device and save to a binary file: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme toshiba vs\-smart\-add\-log /dev/nvme0 \-\-output\-file=log\&.bin \-\-log=0xC0 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite diff --git a/Documentation/nvme-toshiba-vs-smart-add-log.html b/Documentation/nvme-toshiba-vs-smart-add-log.html new file mode 100644 index 0000000..ebe6e83 --- /dev/null +++ b/Documentation/nvme-toshiba-vs-smart-add-log.html @@ -0,0 +1,846 @@ + + + + + +nvme-toshiba-vs-smart-add-log(1) + + + + + +
+
+

SYNOPSIS

+
+
+
'nvme toshiba vs-smart-add-log ' <device> [--log=<NUM>, -l <NUM>]
+                [--namespace-id=<NUM>, -n <NUM>]
+                [--output-file=<FILE>, -o <FILE>]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, sends the Toshiba vendor log +request and either saves the result to a file or dumps the content to stdout.

+

The <device> parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1).

+

The log contents may be associated with the controller, in which case the namespace parameter is ignored.

+

Two logs exist, page 0xC0 (log page directory) and page 0xCA (vendor log page)

+

This will only work on Toshiba devices supporting this feature.

+
+
+
+

OPTIONS

+
+
+
+-l <NUM> +
+
+--log=<NUM> +
+
+

+ Log page: 0xC0 or 0xCA (defaults to 0xCA) +

+
+
+-n <NUM> +
+
+--namespace-id=<NUM> +
+
+-o <FILE> +
+
+--output-file=<FILE> +
+
+

+ Output binary file. Defaults to text-formatted dump to stdout +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Get the current log from the device and dumps it to stdout: +

    +
    +
    +
    # nvme toshiba vs-smart-add-log /dev/nvme0
    +
    +
  • +
  • +

    +Get the contents of log page 0xC0 from the device and save to a binary file: +

    +
    +
    +
    # nvme toshiba vs-smart-add-log /dev/nvme0 --output-file=log.bin --log=0xC0
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-toshiba-vs-smart-add-log.txt b/Documentation/nvme-toshiba-vs-smart-add-log.txt new file mode 100644 index 0000000..8ea4d3e --- /dev/null +++ b/Documentation/nvme-toshiba-vs-smart-add-log.txt @@ -0,0 +1,64 @@ +nvme-toshiba-vs-smart-add-log(1) +================================ + +NAME +---- +nvme-toshiba-vs-smart-add-log - Retrieve a Toshiba device's vendor specific extended SMART log page contents and either save to file or dump the contents. + +SYNOPSIS +-------- +[verse] +'nvme toshiba vs-smart-add-log ' [--log=, -l ] + [--namespace-id=, -n ] + [--output-file=, -o ] + + +DESCRIPTION +----------- +For the NVMe device given, sends the Toshiba vendor log +request and either saves the result to a file or dumps the content to stdout. + +The parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1). + +The log contents may be associated with the controller, in which case the namespace parameter is ignored. + +Two logs exist, page 0xC0 (log page directory) and page 0xCA (vendor log page) + +This will only work on Toshiba devices supporting this feature. + +OPTIONS +------- + +-l :: +--log=:: + Log page: 0xC0 or 0xCA (defaults to 0xCA) + +-n :: +--namespace-id=:: + +-o :: +--output-file=:: + Output binary file. Defaults to text-formatted dump to stdout + + + +EXAMPLES +-------- +* Get the current log from the device and dumps it to stdout: ++ +------------ +# nvme toshiba vs-smart-add-log /dev/nvme0 +------------ ++ + +* Get the contents of log page 0xC0 from the device and save to a binary file: ++ +------------ +# nvme toshiba vs-smart-add-log /dev/nvme0 --output-file=log.bin --log=0xC0 +------------ + +NVME +---- +Part of the nvme-user suite diff --git a/toshiba-nvme.c b/toshiba-nvme.c new file mode 100644 index 0000000..a3b6c13 --- /dev/null +++ b/toshiba-nvme.c @@ -0,0 +1,639 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "linux/nvme_ioctl.h" +#include "nvme.h" +#include "nvme-print.h" +#include "nvme-ioctl.h" +#include "plugin.h" +#include "argconfig.h" +#include "suffix.h" + +#define CREATE_CMD +#include "toshiba-nvme.h" + +static const __u32 OP_SCT_STATUS = 0xE0; +static const __u32 OP_SCT_COMMAND_TRANSFER = 0xE0; +static const __u32 OP_SCT_DATA_TRANSFER = 0xE1; + +static const __u32 DW10_SCT_STATUS_COMMAND = 0x0; +static const __u32 DW10_SCT_COMMAND_TRANSFER = 0x1; + +static const __u32 DW11_SCT_STATUS_COMMAND = 0x0; +static const __u32 DW11_SCT_COMMAND_TRANSFER = 0x0; + +static const __u16 INTERNAL_LOG_ACTION_CODE = 0xFFFB; +static const __u16 CURRENT_LOG_FUNCTION_CODE = 0x0001; +static const __u16 SAVED_LOG_FUNCTION_CODE = 0x0002; + +// A bitmask field for supported devices +typedef enum { + MASK_0 = 1 << 0, + MASK_1 = 1 << 1, + // Future devices can use the remaining 31 bits from this field + // and should use 1 << 2, 1 << 3, etc. + MASK_IGNORE = 0 +} DeviceMask; + +// Internal device codes +typedef enum { + CODE_0 = 0x0D, + CODE_1 = 0x10 +} DeviceCode; + + +static int nvme_sct_op(int fd, __u32 opcode, __u32 cdw10, __u32 cdw11, void* data, __u32 data_len ) +{ + void *metadata = NULL; + const __u32 cdw2 = 0; + const __u32 cdw3 = 0; + const __u32 cdw12 = 0; + const __u32 cdw13 = 0; + const __u32 cdw14 = 0; + const __u32 cdw15 = 0; + const __u32 timeout = 0; + const __u32 metadata_len = 0; + const __u32 namespace_id = 0x0; + const __u32 flags = 0; + const __u32 rsvd = 0; + int err = 0; + __u32 result; + err = nvme_passthru(fd, NVME_IOCTL_ADMIN_CMD, opcode, flags, rsvd, + namespace_id, cdw2, cdw3, cdw10, + cdw11, cdw12, cdw13, cdw14, cdw15, + data_len, data, metadata_len, metadata, + timeout, &result); + return err; +} + +static int nvme_get_sct_status(int fd, __u32 device_mask) +{ + int err; + void* data = NULL; + size_t data_len = 512; + if (posix_memalign(&data, getpagesize(), data_len)) { + err = ENOMEM; + goto end; + } + memset(data, 0, data_len); + err = nvme_sct_op(fd, OP_SCT_STATUS, DW10_SCT_STATUS_COMMAND, DW11_SCT_STATUS_COMMAND, data, data_len); + if (err) { + fprintf(stderr, "%s: SCT status failed :%d\n", __func__, err); + goto end; + } + const unsigned char* status = data; + if (status[0] != 1U) { + // Eek, wrong version in status header + fprintf(stderr, "%s: unexpected value in SCT status[0]:(%x)\n", __func__, status[0]); + err = -1; + errno = EINVAL; + goto end; + } + + // Check if device is supported + if (device_mask != MASK_IGNORE) { + __u32 supported = 0; + switch (status[1]) { + case CODE_0: + supported = (device_mask & MASK_0); + break; + + case CODE_1: + supported = (device_mask & MASK_1); + break; + + default: + break; + }; + if (0 == supported) { + fprintf(stderr, "%s: command unsupported on this device: (0x%x)\n",__func__, status[1]); + err = -1; + errno = EINVAL; + goto end; + } + } +end: + if (data) { + free(data); + } + return err; +} + +static int nvme_sct_command_transfer_log(int fd, bool current) +{ + __u16 action_code = INTERNAL_LOG_ACTION_CODE; + __u16 function_code; + if (current) { + function_code = CURRENT_LOG_FUNCTION_CODE; + } else { + function_code = SAVED_LOG_FUNCTION_CODE; + } + int err; + void* data = NULL; + size_t data_len = 512; + if (posix_memalign(&data, getpagesize(), data_len)) { + err = ENOMEM; + goto end; + } + memset(data, 0, data_len); + memcpy(data, &action_code, sizeof(action_code)); + memcpy(data + 2, &function_code, sizeof(function_code)); + + err = nvme_sct_op(fd, OP_SCT_COMMAND_TRANSFER, DW10_SCT_COMMAND_TRANSFER, DW11_SCT_COMMAND_TRANSFER, data, data_len); + return err; +end: + if (data) { + free(data); + } + return err; +} + +static int nvme_sct_data_transfer(int fd, void* data, size_t data_len, size_t offset) +{ + __u32 lba_count = (data_len) / 512; + if (lba_count) { + // the count is a 0-based value, which seems to mean + // that it's actually last lba + --lba_count; + } + + __u32 dw10 = (offset << 16) | lba_count; + __u32 dw11 = (offset >> 16); + return nvme_sct_op(fd, OP_SCT_DATA_TRANSFER, dw10, dw11, data, data_len); +} + +static int d_raw_to_fd(const unsigned char *buf, unsigned len, int fd) +{ + int written = 0; + int remaining = len; + while (remaining) { + written = write(fd, buf, remaining); + if (written < 0) { + remaining = written; + break; + } else if (written <= remaining) { + remaining -= written; + } else { + // Unexpected overwrite + break; + } + } + // return 0 on success or remaining/error + return remaining; +} + +// Display progress (incoming 0->1.0) +static void progress_runner(float progress) +{ + const size_t barWidth = 70; + + fprintf(stdout, "["); + size_t pos = barWidth * progress; + for (size_t i = 0; i < barWidth; ++i) { + if (i <= pos) { + fprintf(stdout, "="); + } else { + fprintf(stdout, " "); + } + } + fprintf(stdout, "] %d %%\r",(int)(progress * 100.0)); + fflush(stdout); +} + +static int nvme_get_internal_log(int fd, const char* const filename, bool current) +{ + int err; + int o_fd = -1; + void* page_data = NULL; + const size_t page_sector_len = 32; + const size_t page_data_len = page_sector_len * 512;// 32 sectors per page + // By trial and error it seems that the largest transfer chunk size + // is 128 * 32 = 4k sectors = 2MB + const __u32 max_pages = 128; + err = nvme_sct_command_transfer_log(fd, current); + if (err) { + fprintf(stderr, "%s: SCT command transfer failed\n", __func__); + goto end; + } + if (posix_memalign(&page_data, getpagesize(), max_pages * page_data_len)) { + err = ENOMEM; + goto end; + } + memset(page_data, 0, max_pages * page_data_len); + // Read the header to get the last log page - offsets 8->11, 12->15, 16->19 + err = nvme_sct_data_transfer(fd, page_data, page_data_len, 0); + if (err) { + fprintf(stderr, "%s: SCT data transfer failed, page 0\n",__func__); + goto end; + } + const uint32_t* area1_last_page = (const uint32_t*) (page_data + 8); + const uint32_t* area2_last_page = (const uint32_t*) (page_data + 12); + const uint32_t* area3_last_page = (const uint32_t*) (page_data + 16); + uint32_t log_sectors = 0; + // The number of total log sectors is the maximum + 1; + if (*area1_last_page > log_sectors) { + log_sectors = *area1_last_page; + } + if (*area2_last_page > log_sectors) { + log_sectors = *area2_last_page; + } + if (*area3_last_page > log_sectors) { + log_sectors = *area3_last_page; + } + ++log_sectors; + const size_t pages = log_sectors / page_sector_len; + float progress = 0.0; + if (filename == NULL) { + fprintf(stdout, "Page: %u of %zu\n", 0u, pages); + d(page_data, page_data_len, 16, 1); + } else { + progress_runner(progress); + o_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (o_fd < 0) { + fprintf(stderr, "%s: couldn't output file %s\n", __func__, filename); + err = EINVAL; + goto end; + } + err = d_raw_to_fd(page_data, page_data_len, o_fd); + if (err) { + fprintf(stderr, "%s: couldn't write all data to output file\n", __func__); + goto end; + } + } + // Now read the rest + for (size_t i = 1; i < pages;) { + __u32 pages_chunk = max_pages; + if (pages_chunk + i >= pages) { + pages_chunk = pages - i; + } + err = nvme_sct_data_transfer(fd, page_data, pages_chunk * page_data_len, i * page_sector_len); + if (err) { + fprintf(stderr, "%s: SCT data transfer command failed\n", __func__); + goto end; + } + progress = (float) (i) / (float) (pages); + progress_runner(progress); + if (filename == NULL) { + for (unsigned j = 0; j < pages_chunk; ++j) { + fprintf(stdout, "Page: %zu of %zu\n", i + j, pages); + d(page_data + (j * page_data_len), page_data_len, 16, 1); + } + } else { + progress_runner(progress); + err = d_raw_to_fd(page_data, pages_chunk * page_data_len, o_fd); + if (err) { + fprintf(stderr, "%s: couldn't write all data to output file\n", __func__); + goto end; + } + } + i += pages_chunk; + } + progress = 1.0f; + progress_runner(progress); + fprintf(stdout,"\n"); + err = nvme_get_sct_status(fd, MASK_IGNORE); + if (err) { + fprintf(stderr, "%s: bad SCT status\n", __func__); + goto end; + } +end: + if (o_fd >= 0) { + close(o_fd); + } + if (page_data) { + free(page_data); + } + return err; +} + +static int nvme_get_internal_log_file(int fd, const char* const filename, bool current) +{ + int err; + // Check device supported + err = nvme_get_sct_status(fd, MASK_0 | MASK_1); + if (!err) { + err = nvme_get_internal_log(fd, filename, current); + } + return err; +} + +enum LOG_PAGE_C0 { + ERROR_LOG_C0 = 0, + SMART_HEALTH_LOG_C0, + FIRMWARE_SLOT_INFO_C0, + COMMAND_EFFECTS_C0, + DEVICE_SELF_TEST_C0, + LOG_PAGE_DIRECTORY_C0, + SMART_ATTRIBUTES_C0, + NR_SMART_ITEMS_C0, +}; + +struct nvme_xdn_smart_log_c0 { + __u8 items[NR_SMART_ITEMS_C0]; + __u8 resv[512 - NR_SMART_ITEMS_C0]; +}; + +enum LOG_PAGE_CA { + PHYSICAL_NAND_BYTES_WRITTEN_CA = 0, + PHYSICAL_NAND_BYTES_READ_CA = 16, + BAD_NAND_BLOCK_COUNT_CA = 32, + UNCORRECTABLE_READ_ERROR_COUNT_CA = 40, + SOFT_ECC_ERROR_COUNT_CA = 48, + SDD_END_TO_END_CORRECTION_COUNTS_CA = 56, + SYSTEM_DATA_USED_CA = 64, + USER_DATA_ERASE_COUNTS_CA = 65, + REFRESH_COUNT_CA = 73, + PROGRAM_FAIL_COUNT_CA = 81, + USER_ERASE_FAIL_COUNT_CA = 89, + SYSTEM_AREA_ERASE_FAIL_COUNT_CA = 97, + THERMAL_THROTTLING_STATUS_CA = 105, + THERMAL_THROTTLING_COUNT_CA = 106, + PCIE_CORRECTABLE_ERROR_COUNT_CA = 107, + INCOMPLETE_SHUTDOWNS_CA = 115, + FREE_BLOCKS_NORMALISED_CA = 119, + + NR_SMART_ITEMS_CA = 120, +}; + +struct nvme_xdn_smart_log_ca { + __u8 items[NR_SMART_ITEMS_CA]; + __u8 resv[512 - NR_SMART_ITEMS_CA]; +}; + +static unsigned long uint_n_to_ulong(__u8 *data, size_t length) +{ + unsigned long result = 0; + for (size_t i = 0; i < length; i++) { + result *= 256; + result += data[(length -1) - i]; + } + return result; +} + +static float uint_n_to_float(__u8 *data, size_t length) +{ + float result = 0; + for (size_t i = 0; i < length; i++) { + result *= 256.0; + result += data[(length -1) - i]; + } + return result; +} + +static const char* UNITS_B = "B"; +static const char* UNITS_KB = "KiB"; +static const char* UNITS_MB = "MiB"; +static const char* UNITS_GB = "GiB"; + +static float to_readable_units(float raw_bytes, const char** units) +{ + *units = UNITS_B; + float scaled_units = raw_bytes; + if (scaled_units > 102400.0) { // Switch to KiB above 100 KiB + scaled_units /= 1024.0; // Now in KiB. + *units = UNITS_KB; + } + if (scaled_units > 102400.0) { // Switch to MiB above 100 MiB + scaled_units /= 1024.0; // Now in MiB. + *units = UNITS_MB; + } + if (scaled_units > 102400.0) { //Switch to GiB above 100 GiB + scaled_units /= 1024.0; // Now in GiB. + *units = UNITS_GB; + } + return scaled_units; +} + +static void default_show_vendor_log_c0(int fd, __u32 nsid, const char *devname, + struct nvme_xdn_smart_log_c0 *smart) +{ + printf("Vendor Log Page Directory 0xC0 for NVME device:%s namespace-id:%x\n", devname, nsid); + printf("Error Log : %u \n", smart->items[ERROR_LOG_C0]); + printf("SMART Health Log : %u \n", smart->items[SMART_HEALTH_LOG_C0]); + printf("Firmware Slot Info : %u \n", smart->items[FIRMWARE_SLOT_INFO_C0]); + printf("Command Effects : %u \n", smart->items[COMMAND_EFFECTS_C0]); + printf("Device Self Test : %u \n", smart->items[DEVICE_SELF_TEST_C0]); + printf("Log Page Directory : %u \n", smart->items[LOG_PAGE_DIRECTORY_C0]); + printf("SMART Attributes : %u \n", smart->items[SMART_ATTRIBUTES_C0]); +} + +static void default_show_vendor_log_ca(int fd, __u32 nsid, const char *devname, + struct nvme_xdn_smart_log_ca *smart) +{ + printf("Vendor Log Page 0xCA for NVME device:%s namespace-id:%x\n", devname, nsid); + const char* units; + float media_units; + media_units = uint_n_to_float(&smart->items[PHYSICAL_NAND_BYTES_WRITTEN_CA], 16); // units are bytes + media_units = to_readable_units(media_units, &units); + printf("Total data written to NAND : %.1f %s\n", media_units, units); + media_units = uint_n_to_float(&smart->items[PHYSICAL_NAND_BYTES_READ_CA], 16); // units are bytes + media_units = to_readable_units(media_units, &units); + printf("Total data read from NAND : %.1f %s\n", media_units, units); + printf("Bad NAND Block Count (Normalised) : %lu\n", uint_n_to_ulong(&smart->items[BAD_NAND_BLOCK_COUNT_CA], 2)); + printf("Bad NAND Block Count (Raw) : %lu\n", uint_n_to_ulong(&smart->items[BAD_NAND_BLOCK_COUNT_CA+2], 6)); + printf("Uncorrectable Read Error Count : %lu\n", uint_n_to_ulong(&smart->items[UNCORRECTABLE_READ_ERROR_COUNT_CA], 8)); + printf("Soft ECC Error Count : %lu\n", uint_n_to_ulong(&smart->items[SOFT_ECC_ERROR_COUNT_CA], 8)); + printf("End-to-end Error Count (Detected) : %lu\n", uint_n_to_ulong(&smart->items[SDD_END_TO_END_CORRECTION_COUNTS_CA], 4)); + printf("End-to-end Error Count (Corrected) : %lu\n", uint_n_to_ulong(&smart->items[SDD_END_TO_END_CORRECTION_COUNTS_CA + 4], 4)); + printf("System Data Used : %u %%\n", smart->items[SYSTEM_DATA_USED_CA]); + printf("User Data Erase Count (Max) : %lu\n", uint_n_to_ulong(&smart->items[USER_DATA_ERASE_COUNTS_CA], 4)); + printf("User Data Erase Count (Min) : %lu\n", uint_n_to_ulong(&smart->items[USER_DATA_ERASE_COUNTS_CA + 4], 4)); + printf("Refresh Count : %lu\n", uint_n_to_ulong(&smart->items[REFRESH_COUNT_CA], 8)); + printf("Program Fail Count (Normalised) : %lu\n", uint_n_to_ulong(&smart->items[PROGRAM_FAIL_COUNT_CA], 2)); + printf("Program Fail Count (Raw) : %lu\n", uint_n_to_ulong(&smart->items[PROGRAM_FAIL_COUNT_CA + 2], 6)); + printf("User Data Erase Fail Count (Normalised) : %lu\n", uint_n_to_ulong(&smart->items[USER_ERASE_FAIL_COUNT_CA], 2)); + printf("User Data Erase Fail Count (Raw) : %lu\n", uint_n_to_ulong(&smart->items[USER_ERASE_FAIL_COUNT_CA + 2], 6)); + printf("System Area Erase Fail Count (Normalised): %lu\n", uint_n_to_ulong(&smart->items[SYSTEM_AREA_ERASE_FAIL_COUNT_CA], 2)); + printf("System Area Erase Fail Count (Raw) : %lu\n", uint_n_to_ulong(&smart->items[SYSTEM_AREA_ERASE_FAIL_COUNT_CA + 2], 6)); + printf("Thermal Throttling Status : %u \n", smart->items[THERMAL_THROTTLING_STATUS_CA]); + printf("Thermal Throttling Count : %u \n", smart->items[THERMAL_THROTTLING_COUNT_CA]); + printf("PCIe Correctable Error Count : %lu\n", uint_n_to_ulong(&smart->items[PCIE_CORRECTABLE_ERROR_COUNT_CA], 8)); + printf("Incomplete Shutdowns : %lu\n", uint_n_to_ulong(&smart->items[INCOMPLETE_SHUTDOWNS_CA], 4)); + printf("Free blocks : %u %%\n", smart->items[FREE_BLOCKS_NORMALISED_CA]); +} + +static int nvme_get_vendor_log(int fd, __u32 namespace_id, int log_page, const char* const filename) +{ + int err; + void* log = NULL; + size_t log_len = 512; + if (posix_memalign(&log, getpagesize(), log_len)) { + err = ENOMEM; + goto end; + } + // Check device supported + err = nvme_get_sct_status(fd, MASK_0 | MASK_1); + if (err) { + goto end; + } + err = nvme_get_log(fd, namespace_id, log_page, + log_len, log); + if (err) { + fprintf(stderr, "%s: couldn't get log 0x%x\n", __func__, log_page); + goto end; + } + if (filename) { + int o_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + if (o_fd < 0) { + fprintf(stderr, "%s: couldn't output file %s\n", __func__, filename); + err = EINVAL; + goto end; + } + err = d_raw_to_fd(log, log_len, o_fd); + if (err) { + fprintf(stderr, "%s: couldn't write all data to output file %s\n", __func__, filename); + // Attempt following close + } + if (close(o_fd)) { + err = errno; + goto end; + } + } else { + if (log_page == 0xc0) { + default_show_vendor_log_c0(fd, namespace_id, devicename, (struct nvme_xdn_smart_log_c0 *)log); + } else if (log_page == 0xca) { + default_show_vendor_log_ca(fd, namespace_id, devicename, (struct nvme_xdn_smart_log_ca *)log); + } else { + d(log, sizeof(log),16,1); + } + } +end: + if (log) { + free(log); + } + return err; +} + +static int vendor_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + int err; + int fd; + char *desc = "Get extended SMART information and show it."; + const char *namespace = "(optional) desired namespace"; + const char *output_file = "(optional) binary output filename"; + const char *log = "(optional) log ID (0xC0, or 0xCA), default 0xCA"; + struct config { + __u32 namespace_id; + const char* output_file; + int log; + }; + struct config cfg = { + .namespace_id = 0xffffffff, + .output_file = NULL, + .log = 0xca + }; + const struct argconfig_commandline_options command_line_options[] = { + {"namespace-id", 'n', "NUM", CFG_POSITIVE, &cfg.namespace_id, required_argument, namespace}, + {"output-file", 'o', "FILE", CFG_STRING, &cfg.output_file, required_argument, output_file}, + {"log", 'l', "NUM", CFG_POSITIVE, &cfg.log, required_argument, log}, + {NULL} + }; + fd = parse_and_open(argc, argv, desc, command_line_options, &cfg, sizeof(cfg)); + if (fd < 0) { + fprintf(stderr,"%s: failed to parse arguments\n", __func__); + err = EINVAL; + goto end; + } + if ((cfg.log != 0xC0) && (cfg.log != 0xCA)) { + fprintf(stderr, "%s: invalid log page 0x%x - should be 0xC0 or 0xCA\n", __func__, cfg.log); + err = EINVAL; + goto end; + } + err = nvme_get_vendor_log(fd, cfg.namespace_id, cfg.log, cfg.output_file); + if (err) { + fprintf(stderr, "%s: couldn't get vendor log 0x%x\n", __func__, cfg.log); + goto end; + } +end: + if (err > 0) { + fprintf(stderr, "%s: NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(err), err); + } + return err; +} + +static int internal_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + int err; + int fd; + char *desc = "Get internal status log and show it."; + const char *output_file = "(optional) binary output filename"; + const char *prev_log = "(optional) use previous log. Otherwise uses current log."; + struct config { + const char* output_file; + bool prev_log; + }; + struct config cfg = { + .output_file = NULL, + .prev_log = false + }; + const struct argconfig_commandline_options command_line_options[] = { + {"output-file", 'o', "FILE", CFG_STRING, &cfg.output_file, required_argument, output_file}, + {"prev-log", 'p', "", CFG_NONE, &cfg.prev_log, no_argument, prev_log}, + {NULL} + }; + fd = parse_and_open(argc, argv, desc, command_line_options, &cfg, sizeof(cfg)); + if (fd < 0) { + fprintf(stderr,"%s: failed to parse arguments\n", __func__); + err = EINVAL; + goto end; + } + if (cfg.prev_log) { + printf("Getting previous log\n"); + } else { + printf("Getting current log\n"); + } + err = nvme_get_internal_log_file(fd, cfg.output_file, !cfg.prev_log); + if (err) { + fprintf(stderr, "%s: couldn't get fw log \n", __func__); + } +end: + if (err > 0) { + fprintf(stderr, "%s: NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(err), err); + } + return err; +} + +static int clear_correctable_errors(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + int err; + int fd; + char *desc = "Clear PCIe correctable error count."; + const struct argconfig_commandline_options command_line_options[] = { + {NULL} + }; + fd = parse_and_open(argc, argv, desc, command_line_options, NULL, 0); + if (fd < 0) { + fprintf(stderr,"%s: failed to parse arguments\n", __func__); + err = EINVAL; + goto end; + } + // Check device supported + err = nvme_get_sct_status(fd, MASK_0 | MASK_1); + if (err) { + goto end; + } + const __u32 namespace_id = 0xFFFFFFFF; + const __u32 feature_id = 0xCA; + const __u32 value = 1; // Bit0 - reset clear PCIe correctable count + const __u32 cdw12 = 0; + const bool save = false; + __u32 result; + err = nvme_set_feature(fd, namespace_id, feature_id, value, cdw12, save, + 0, NULL, &result); + if (err) { + fprintf(stderr, "%s: couldn't clear PCIe correctable errors \n", __func__); + } +end: + if (err > 0) { + fprintf(stderr, "%s: NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(err), err); + } + return err; +} diff --git a/toshiba-nvme.h b/toshiba-nvme.h new file mode 100644 index 0000000..cecbbf3 --- /dev/null +++ b/toshiba-nvme.h @@ -0,0 +1,20 @@ +#undef CMD_INC_FILE +#define CMD_INC_FILE toshiba-nvme + +#if !defined(TOSHIBA_NVME) || defined(CMD_HEADER_MULTI_READ) +#define TOSHIBA_NVME + +#include "cmd.h" +#include "plugin.h" + +PLUGIN(NAME("toshiba", "Toshiba NVME plugin"), + COMMAND_LIST( + ENTRY("vs-smart-add-log", "Extended SMART information", vendor_log) + ENTRY("vs-internal-log", "Get Internal Log", internal_log) + ENTRY("clear-pcie-correctable-errors", "Clear PCIe correctable error count", clear_correctable_errors) + ) +); + +#endif + +#include "define_cmd.h" -- 2.51.0