Page MenuHomeFreeBSD

D18403.1793450297.diff
No OneTemporary

Authored By
Unknown
Size
66 KB
Referenced Files
None
Subscribers
None

D18403.1793450297.diff

Index: sbin/nvmecontrol/Makefile
===================================================================
--- sbin/nvmecontrol/Makefile
+++ sbin/nvmecontrol/Makefile
@@ -3,7 +3,8 @@
PACKAGE=runtime
PROG= nvmecontrol
SRCS= nvmecontrol.c devlist.c firmware.c format.c identify.c identify_ext.c logpage.c \
- perftest.c reset.c ns.c nvme_util.c power.c nc_util.c wdc.c
+ perftest.c reset.c ns.c nvme_util.c power.c nc_util.c
+SRCS+= wdc.c intel.c
MAN= nvmecontrol.8
.PATH: ${SRCTOP}/sys/dev/nvme
Index: sbin/nvmecontrol/devlist.c
===================================================================
--- sbin/nvmecontrol/devlist.c
+++ sbin/nvmecontrol/devlist.c
@@ -43,13 +43,8 @@
#include "nvmecontrol.h"
-static void
-devlist_usage(void)
-{
- fprintf(stderr, "usage:\n");
- fprintf(stderr, DEVLIST_USAGE);
- exit(1);
-}
+#define DEVLIST_USAGE \
+ "devlist\n"
static inline uint32_t
ns_get_sector_size(struct nvme_namespace_data *nsdata)
@@ -64,8 +59,8 @@
return (1 << lbads);
}
-void
-devlist(int argc, char *argv[])
+static void
+devlist(struct nvme_function *nf, int argc, char *argv[])
{
struct nvme_controller_data cdata;
struct nvme_namespace_data nsdata;
@@ -77,7 +72,7 @@
while ((ch = getopt(argc, argv, "")) != -1) {
switch ((char)ch) {
default:
- devlist_usage();
+ usage(nf);
}
}
@@ -124,3 +119,5 @@
exit(1);
}
+
+NVME_COMMAND(top, devlist, devlist, DEVLIST_USAGE);
Index: sbin/nvmecontrol/firmware.c
===================================================================
--- sbin/nvmecontrol/firmware.c
+++ sbin/nvmecontrol/firmware.c
@@ -50,6 +50,9 @@
#include "nvmecontrol.h"
+#define FIRMWARE_USAGE \
+ "firmware [-s slot] [-f path_to_firmware] [-a] <controller id>\n"
+
static int
slot_has_valid_firmware(int fd, int slot)
{
@@ -171,15 +174,7 @@
}
static void
-firmware_usage(void)
-{
- fprintf(stderr, "usage:\n");
- fprintf(stderr, FIRMWARE_USAGE);
- exit(1);
-}
-
-void
-firmware(int argc, char *argv[])
+firmware(struct nvme_function *nf, int argc, char *argv[])
{
int fd = -1, slot = 0;
int a_flag, s_flag, f_flag;
@@ -206,18 +201,18 @@
fprintf(stderr,
"\"%s\" not valid slot.\n",
optarg);
- firmware_usage();
+ usage(nf);
} else if (slot == 0) {
fprintf(stderr,
"0 is not a valid slot number. "
"Slot numbers start at 1.\n");
- firmware_usage();
+ usage(nf);
} else if (slot > 7) {
fprintf(stderr,
"Slot number %s specified which is "
"greater than max allowed slot number of "
"7.\n", optarg);
- firmware_usage();
+ usage(nf);
}
s_flag = true;
break;
@@ -230,20 +225,20 @@
/* Check that a controller (and not a namespace) was specified. */
if (optind >= argc || strstr(argv[optind], NVME_NS_PREFIX) != NULL)
- firmware_usage();
+ usage(nf);
if (!f_flag && !a_flag) {
fprintf(stderr,
"Neither a replace ([-f path_to_firmware]) nor "
"activate ([-a]) firmware image action\n"
"was specified.\n");
- firmware_usage();
+ usage(nf);
}
if (!f_flag && a_flag && slot == 0) {
fprintf(stderr,
"Slot number to activate not specified.\n");
- firmware_usage();
+ usage(nf);
}
controller = argv[optind];
@@ -338,3 +333,5 @@
close(fd);
exit(0);
}
+
+NVME_COMMAND(top, firmware, firmware, FIRMWARE_USAGE);
Index: sbin/nvmecontrol/format.c
===================================================================
--- sbin/nvmecontrol/format.c
+++ sbin/nvmecontrol/format.c
@@ -43,16 +43,11 @@
#include "nvmecontrol.h"
-static void
-format_usage(void)
-{
- fprintf(stderr, "usage:\n");
- fprintf(stderr, FORMAT_USAGE);
- exit(1);
-}
+#define FORMAT_USAGE \
+ "format [-f fmt] [-m mset] [-p pi] [-l pil] [-E] [-C] <controller id|namespace id>\n"
-void
-format(int argc, char *argv[])
+static void
+format(struct nvme_function *nf, int argc, char *argv[])
{
struct nvme_controller_data cd;
struct nvme_namespace_data nsd;
@@ -64,7 +59,7 @@
int lbaf = -1, mset = -1, pi = -1, pil = -1, ses = 0;
if (argc < 2)
- format_usage();
+ usage(nf);
while ((ch = getopt(argc, argv, "f:m:p:l:EC")) != -1) {
switch ((char)ch) {
@@ -91,13 +86,13 @@
ses = 2;
break;
default:
- format_usage();
+ usage(nf);
}
}
/* Check that a controller or namespace was specified. */
if (optind >= argc)
- format_usage();
+ usage(nf);
target = argv[optind];
/*
@@ -185,3 +180,5 @@
close(fd);
exit(0);
}
+
+NVME_COMMAND(top, format, format, FORMAT_USAGE);
Index: sbin/nvmecontrol/identify.c
===================================================================
--- sbin/nvmecontrol/identify.c
+++ sbin/nvmecontrol/identify.c
@@ -43,6 +43,9 @@
#include "nvmecontrol.h"
#include "nvmecontrol_ext.h"
+#define IDENTIFY_USAGE \
+ "identify [-x [-v]] <controller id|namespace id>\n"
+
static void
print_namespace(struct nvme_namespace_data *nsdata)
{
@@ -147,15 +150,7 @@
}
static void
-identify_usage(void)
-{
- fprintf(stderr, "usage:\n");
- fprintf(stderr, IDENTIFY_USAGE);
- exit(1);
-}
-
-static void
-identify_ctrlr(int argc, char *argv[])
+identify_ctrlr(struct nvme_function *nf, int argc, char *argv[])
{
struct nvme_controller_data cdata;
int ch, fd, hexflag = 0, hexlength;
@@ -170,13 +165,13 @@
hexflag = 1;
break;
default:
- identify_usage();
+ usage(nf);
}
}
/* Check that a controller was specified. */
if (optind >= argc)
- identify_usage();
+ usage(nf);
open_dev(argv[optind], &fd, 1, 1);
read_controller_data(fd, &cdata);
@@ -194,7 +189,7 @@
if (verboseflag == 1) {
fprintf(stderr, "-v not currently supported without -x\n");
- identify_usage();
+ usage(nf);
}
nvme_print_controller(&cdata);
@@ -202,7 +197,7 @@
}
static void
-identify_ns(int argc, char *argv[])
+identify_ns(struct nvme_function *nf,int argc, char *argv[])
{
struct nvme_namespace_data nsdata;
char path[64];
@@ -219,13 +214,13 @@
hexflag = 1;
break;
default:
- identify_usage();
+ usage(nf);
}
}
/* Check that a namespace was specified. */
if (optind >= argc)
- identify_usage();
+ usage(nf);
/*
* Check if the specified device node exists before continuing.
@@ -258,26 +253,26 @@
if (verboseflag == 1) {
fprintf(stderr, "-v not currently supported without -x\n");
- identify_usage();
+ usage(nf);
}
print_namespace(&nsdata);
exit(0);
}
-void
-identify(int argc, char *argv[])
+static void
+identify(struct nvme_function *nf, int argc, char *argv[])
{
char *target;
if (argc < 2)
- identify_usage();
+ usage(nf);
while (getopt(argc, argv, "vx") != -1) ;
/* Check that a controller or namespace was specified. */
if (optind >= argc)
- identify_usage();
+ usage(nf);
target = argv[optind];
@@ -289,7 +284,9 @@
* otherwise, consider it a controller.
*/
if (strstr(target, NVME_NS_PREFIX) == NULL)
- identify_ctrlr(argc, argv);
+ identify_ctrlr(nf, argc, argv);
else
- identify_ns(argc, argv);
+ identify_ns(nf, argc, argv);
}
+
+NVME_COMMAND(top, identify, identify, IDENTIFY_USAGE);
Index: sbin/nvmecontrol/intel.c
===================================================================
--- /dev/null
+++ sbin/nvmecontrol/intel.c
@@ -0,0 +1,198 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 EMC Corp.
+ * All rights reserved.
+ *
+ * Copyright (C) 2012-2013 Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/ioccom.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/endian.h>
+
+#include "nvmecontrol.h"
+
+/*
+ * Intel specific log pages from
+ * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf
+ *
+ * Though the version as of this date has a typo for the size of log page 0xca,
+ * offset 147: it is only 1 byte, not 6.
+ */
+static void
+print_intel_temp_stats(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
+{
+ struct intel_log_temp_stats *temp = buf;
+
+ printf("Intel Temperature Log\n");
+ printf("=====================\n");
+
+ printf("Current: ");
+ print_temp(temp->current);
+ printf("Overtemp Last Flags %#jx\n", (uintmax_t)temp->overtemp_flag_last);
+ printf("Overtemp Lifetime Flags %#jx\n", (uintmax_t)temp->overtemp_flag_life);
+ printf("Max Temperature ");
+ print_temp(temp->max_temp);
+ printf("Min Temperature ");
+ print_temp(temp->min_temp);
+ printf("Max Operating Temperature ");
+ print_temp(temp->max_oper_temp);
+ printf("Min Operating Temperature ");
+ print_temp(temp->min_oper_temp);
+ printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset);
+}
+
+/*
+ * Format from Table 22, section 5.7 IO Command Latency Statistics.
+ * Read and write stats pages have identical encoding.
+ */
+static void
+print_intel_read_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
+{
+ const char *walker = buf;
+ int i;
+
+ printf("Major: %d\n", le16dec(walker + 0));
+ printf("Minor: %d\n", le16dec(walker + 2));
+ for (i = 0; i < 32; i++)
+ printf("%4dus-%4dus: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 4 + i * 4));
+ for (i = 1; i < 32; i++)
+ printf("%4dms-%4dms: %ju\n", i, i + 1, (uintmax_t)le32dec(walker + 132 + i * 4));
+ for (i = 1; i < 32; i++)
+ printf("%4dms-%4dms: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 256 + i * 4));
+}
+
+static void
+print_intel_read_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
+{
+
+ printf("Intel Read Latency Log\n");
+ printf("======================\n");
+ print_intel_read_write_lat_log(cdata, buf, size);
+}
+
+static void
+print_intel_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
+{
+
+ printf("Intel Write Latency Log\n");
+ printf("=======================\n");
+ print_intel_read_write_lat_log(cdata, buf, size);
+}
+
+/*
+ * Table 19. 5.4 SMART Attributes. Samsung also implements this and some extra data not documented.
+ */
+static void
+print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
+{
+ uint8_t *walker = buf;
+ uint8_t *end = walker + 150;
+ const char *name;
+ uint64_t raw;
+ uint8_t normalized;
+
+ static struct kv_name kv[] =
+ {
+ { 0xab, "Program Fail Count" },
+ { 0xac, "Erase Fail Count" },
+ { 0xad, "Wear Leveling Count" },
+ { 0xb8, "End to End Error Count" },
+ { 0xc7, "CRC Error Count" },
+ { 0xe2, "Timed: Media Wear" },
+ { 0xe3, "Timed: Host Read %" },
+ { 0xe4, "Timed: Elapsed Time" },
+ { 0xea, "Thermal Throttle Status" },
+ { 0xf0, "Retry Buffer Overflows" },
+ { 0xf3, "PLL Lock Loss Count" },
+ { 0xf4, "NAND Bytes Written" },
+ { 0xf5, "Host Bytes Written" },
+ };
+
+ printf("Additional SMART Data Log\n");
+ printf("=========================\n");
+ /*
+ * walker[0] = Key
+ * walker[1,2] = reserved
+ * walker[3] = Normalized Value
+ * walker[4] = reserved
+ * walker[5..10] = Little Endian Raw value
+ * (or other represenations)
+ * walker[11] = reserved
+ */
+ while (walker < end) {
+ name = kv_lookup(kv, nitems(kv), *walker);
+ normalized = walker[3];
+ raw = le48dec(walker + 5);
+ switch (*walker){
+ case 0:
+ break;
+ case 0xad:
+ printf("%-32s: %3d min: %u max: %u ave: %u\n", name, normalized,
+ le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9));
+ break;
+ case 0xe2:
+ printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0);
+ break;
+ case 0xea:
+ printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6));
+ break;
+ default:
+ printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw);
+ break;
+ }
+ walker += 12;
+ }
+}
+
+NVME_LOGPAGE(intel_temp,
+ INTEL_LOG_TEMP_STATS, "intel", "Temperature Stats",
+ print_intel_temp_stats, sizeof(struct intel_log_temp_stats));
+NVME_LOGPAGE(intel_rlat,
+ INTEL_LOG_READ_LAT_LOG, "intel", "Read Latencies",
+ print_intel_read_lat_log, DEFAULT_SIZE);
+NVME_LOGPAGE(intel_wlat,
+ INTEL_LOG_WRITE_LAT_LOG, "intel", "Write Latencies",
+ print_intel_write_lat_log, DEFAULT_SIZE);
+NVME_LOGPAGE(intel_smart,
+ INTEL_LOG_ADD_SMART, "intel", "Extra Health/SMART Data",
+ print_intel_add_smart, DEFAULT_SIZE);
+NVME_LOGPAGE(samsung_smart, /* NOTE, this will be deleted before 13.0 */
+ INTEL_LOG_ADD_SMART, "samsung", "Extra Health/SMART Data",
+ print_intel_add_smart, DEFAULT_SIZE);
Index: sbin/nvmecontrol/logpage.c
===================================================================
--- sbin/nvmecontrol/logpage.c
+++ sbin/nvmecontrol/logpage.c
@@ -48,18 +48,14 @@
#include "nvmecontrol.h"
-#define DEFAULT_SIZE (4096)
-#define MAX_FW_SLOTS (7)
+SET_DECLARE(logpage, struct logpage_function);
-typedef void (*print_fn_t)(const struct nvme_controller_data *cdata, void *buf, uint32_t size);
+#define LOGPAGE_USAGE \
+ "logpage <-p page_id> [-b] [-v vendor] [-x] <controller id|namespace id>\n" \
-struct kv_name
-{
- uint32_t key;
- const char *name;
-};
+#define MAX_FW_SLOTS (7)
-static const char *
+const char *
kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
{
static char bad[32];
@@ -195,7 +191,7 @@
}
}
-static void
+void
print_temp(uint16_t t)
{
printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67);
@@ -311,605 +307,41 @@
}
}
-/*
- * Intel specific log pages from
- * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf
- *
- * Though the version as of this date has a typo for the size of log page 0xca,
- * offset 147: it is only 1 byte, not 6.
- */
-static void
-print_intel_temp_stats(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
-{
- struct intel_log_temp_stats *temp = buf;
-
- printf("Intel Temperature Log\n");
- printf("=====================\n");
-
- printf("Current: ");
- print_temp(temp->current);
- printf("Overtemp Last Flags %#jx\n", (uintmax_t)temp->overtemp_flag_last);
- printf("Overtemp Lifetime Flags %#jx\n", (uintmax_t)temp->overtemp_flag_life);
- printf("Max Temperature ");
- print_temp(temp->max_temp);
- printf("Min Temperature ");
- print_temp(temp->min_temp);
- printf("Max Operating Temperature ");
- print_temp(temp->max_oper_temp);
- printf("Min Operating Temperature ");
- print_temp(temp->min_oper_temp);
- printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset);
-}
-
-/*
- * Format from Table 22, section 5.7 IO Command Latency Statistics.
- * Read and write stats pages have identical encoding.
- */
-static void
-print_intel_read_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
-{
- const char *walker = buf;
- int i;
-
- printf("Major: %d\n", le16dec(walker + 0));
- printf("Minor: %d\n", le16dec(walker + 2));
- for (i = 0; i < 32; i++)
- printf("%4dus-%4dus: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 4 + i * 4));
- for (i = 1; i < 32; i++)
- printf("%4dms-%4dms: %ju\n", i, i + 1, (uintmax_t)le32dec(walker + 132 + i * 4));
- for (i = 1; i < 32; i++)
- printf("%4dms-%4dms: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 256 + i * 4));
-}
-
-static void
-print_intel_read_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
-{
-
- printf("Intel Read Latency Log\n");
- printf("======================\n");
- print_intel_read_write_lat_log(cdata, buf, size);
-}
-
-static void
-print_intel_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
-{
-
- printf("Intel Write Latency Log\n");
- printf("=======================\n");
- print_intel_read_write_lat_log(cdata, buf, size);
-}
-
-/*
- * Table 19. 5.4 SMART Attributes. Samsung also implements this and some extra data not documented.
- */
-static void
-print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
-{
- uint8_t *walker = buf;
- uint8_t *end = walker + 150;
- const char *name;
- uint64_t raw;
- uint8_t normalized;
-
- static struct kv_name kv[] =
- {
- { 0xab, "Program Fail Count" },
- { 0xac, "Erase Fail Count" },
- { 0xad, "Wear Leveling Count" },
- { 0xb8, "End to End Error Count" },
- { 0xc7, "CRC Error Count" },
- { 0xe2, "Timed: Media Wear" },
- { 0xe3, "Timed: Host Read %" },
- { 0xe4, "Timed: Elapsed Time" },
- { 0xea, "Thermal Throttle Status" },
- { 0xf0, "Retry Buffer Overflows" },
- { 0xf3, "PLL Lock Loss Count" },
- { 0xf4, "NAND Bytes Written" },
- { 0xf5, "Host Bytes Written" },
- };
-
- printf("Additional SMART Data Log\n");
- printf("=========================\n");
- /*
- * walker[0] = Key
- * walker[1,2] = reserved
- * walker[3] = Normalized Value
- * walker[4] = reserved
- * walker[5..10] = Little Endian Raw value
- * (or other represenations)
- * walker[11] = reserved
- */
- while (walker < end) {
- name = kv_lookup(kv, nitems(kv), *walker);
- normalized = walker[3];
- raw = le48dec(walker + 5);
- switch (*walker){
- case 0:
- break;
- case 0xad:
- printf("%-32s: %3d min: %u max: %u ave: %u\n", name, normalized,
- le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9));
- break;
- case 0xe2:
- printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0);
- break;
- case 0xea:
- printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6));
- break;
- default:
- printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw);
- break;
- }
- walker += 12;
- }
-}
-
-/*
- * HGST's 0xc1 page. This is a grab bag of additional data. Please see
- * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf
- * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf
- * Appendix A for details
- */
-
-typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
-
-struct subpage_print
-{
- uint16_t key;
- subprint_fn_t fn;
-};
-
-static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
-static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
-static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
-static void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
-static void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
-static void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
-static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
-static void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
-static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
-static void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
-
-static struct subpage_print hgst_subpage[] = {
- { 0x02, print_hgst_info_write_errors },
- { 0x03, print_hgst_info_read_errors },
- { 0x05, print_hgst_info_verify_errors },
- { 0x10, print_hgst_info_self_test },
- { 0x15, print_hgst_info_background_scan },
- { 0x30, print_hgst_info_erase_errors },
- { 0x31, print_hgst_info_erase_counts },
- { 0x32, print_hgst_info_temp_history },
- { 0x37, print_hgst_info_ssd_perf },
- { 0x38, print_hgst_info_firmware_load },
-};
-
-/* Print a subpage that is basically just key value pairs */
-static void
-print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size,
- const struct kv_name *kv, size_t kv_count)
-{
- uint8_t *wsp, *esp;
- uint16_t ptype;
- uint8_t plen;
- uint64_t param;
- int i;
-
- wsp = buf;
- esp = wsp + size;
- while (wsp < esp) {
- ptype = le16dec(wsp);
- wsp += 2;
- wsp++; /* Flags, just ignore */
- plen = *wsp++;
- param = 0;
- for (i = 0; i < plen; i++)
- param |= (uint64_t)*wsp++ << (i * 8);
- printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param);
- }
-}
-
-static void
-print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
-{
- static struct kv_name kv[] =
- {
- { 0x0000, "Corrected Without Delay" },
- { 0x0001, "Corrected Maybe Delayed" },
- { 0x0002, "Re-Writes" },
- { 0x0003, "Errors Corrected" },
- { 0x0004, "Correct Algorithm Used" },
- { 0x0005, "Bytes Processed" },
- { 0x0006, "Uncorrected Errors" },
- { 0x8000, "Flash Write Commands" },
- { 0x8001, "HGST Special" },
- };
-
- printf("Write Errors Subpage:\n");
- print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
-}
-
-static void
-print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
-{
- static struct kv_name kv[] =
- {
- { 0x0000, "Corrected Without Delay" },
- { 0x0001, "Corrected Maybe Delayed" },
- { 0x0002, "Re-Reads" },
- { 0x0003, "Errors Corrected" },
- { 0x0004, "Correct Algorithm Used" },
- { 0x0005, "Bytes Processed" },
- { 0x0006, "Uncorrected Errors" },
- { 0x8000, "Flash Read Commands" },
- { 0x8001, "XOR Recovered" },
- { 0x8002, "Total Corrected Bits" },
- };
-
- printf("Read Errors Subpage:\n");
- print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
-}
-
-static void
-print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
-{
- static struct kv_name kv[] =
- {
- { 0x0000, "Corrected Without Delay" },
- { 0x0001, "Corrected Maybe Delayed" },
- { 0x0002, "Re-Reads" },
- { 0x0003, "Errors Corrected" },
- { 0x0004, "Correct Algorithm Used" },
- { 0x0005, "Bytes Processed" },
- { 0x0006, "Uncorrected Errors" },
- { 0x8000, "Commands Processed" },
- };
-
- printf("Verify Errors Subpage:\n");
- print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
-}
-
-static void
-print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
-{
- size_t i;
- uint8_t *walker = buf;
- uint16_t code, hrs;
- uint32_t lba;
-
- printf("Self Test Subpage:\n");
- for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */
- code = le16dec(walker);
- walker += 2;
- walker++; /* Ignore fixed flags */
- if (*walker == 0) /* Last entry is zero length */
- break;
- if (*walker++ != 0x10) {
- printf("Bad length for self test report\n");
- return;
- }
- printf(" %-30s: %d\n", "Recent Test", code);
- printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf);
- printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7);
- walker++;
- printf(" %-28s: %#x\n", "Self-Test Number", *walker++);
- hrs = le16dec(walker);
- walker += 2;
- lba = le32dec(walker);
- walker += 4;
- printf(" %-28s: %u\n", "Total Power On Hrs", hrs);
- printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba);
- printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf);
- printf(" %-28s: %#x\n", "Additional Sense Code", *walker++);
- printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++);
- printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++);
- }
-}
-
-static void
-print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
-{
- uint8_t *walker = buf;
- uint8_t status;
- uint16_t code, nscan, progress;
- uint32_t pom, nand;
-
- printf("Background Media Scan Subpage:\n");
- /* Decode the header */
- code = le16dec(walker);
- walker += 2;
- walker++; /* Ignore fixed flags */
- if (*walker++ != 0x10) {
- printf("Bad length for background scan header\n");
- return;
- }
- if (code != 0) {
- printf("Expceted code 0, found code %#x\n", code);
- return;
- }
- pom = le32dec(walker);
- walker += 4;
- walker++; /* Reserved */
- status = *walker++;
- nscan = le16dec(walker);
- walker += 2;
- progress = le16dec(walker);
- walker += 2;
- walker += 6; /* Reserved */
- printf(" %-30s: %d\n", "Power On Minutes", pom);
- printf(" %-30s: %x (%s)\n", "BMS Status", status,
- status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown")));
- printf(" %-30s: %d\n", "Number of BMS", nscan);
- printf(" %-30s: %d\n", "Progress Current BMS", progress);
- /* Report retirements */
- if (walker - (uint8_t *)buf != 20) {
- printf("Coding error, offset not 20\n");
- return;
- }
- size -= 20;
- printf(" %-30s: %d\n", "BMS retirements", size / 0x18);
- while (size > 0) {
- code = le16dec(walker);
- walker += 2;
- walker++;
- if (*walker++ != 0x14) {
- printf("Bad length parameter\n");
- return;
- }
- pom = le32dec(walker);
- walker += 4;
- /*
- * Spec sheet says the following are hard coded, if true, just
- * print the NAND retirement.
- */
- if (walker[0] == 0x41 &&
- walker[1] == 0x0b &&
- walker[2] == 0x01 &&
- walker[3] == 0x00 &&
- walker[4] == 0x00 &&
- walker[5] == 0x00 &&
- walker[6] == 0x00 &&
- walker[7] == 0x00) {
- walker += 8;
- walker += 4; /* Skip reserved */
- nand = le32dec(walker);
- walker += 4;
- printf(" %-30s: %d\n", "Retirement number", code);
- printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand);
- } else {
- printf("Parameter %#x entry corrupt\n", code);
- walker += 16;
- }
- }
-}
-
-static void
-print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
-{
- static struct kv_name kv[] =
- {
- { 0x0000, "Corrected Without Delay" },
- { 0x0001, "Corrected Maybe Delayed" },
- { 0x0002, "Re-Erase" },
- { 0x0003, "Errors Corrected" },
- { 0x0004, "Correct Algorithm Used" },
- { 0x0005, "Bytes Processed" },
- { 0x0006, "Uncorrected Errors" },
- { 0x8000, "Flash Erase Commands" },
- { 0x8001, "Mfg Defect Count" },
- { 0x8002, "Grown Defect Count" },
- { 0x8003, "Erase Count -- User" },
- { 0x8004, "Erase Count -- System" },
- };
-
- printf("Erase Errors Subpage:\n");
- print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
-}
-
-static void
-print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
-{
- /* My drive doesn't export this -- so not coding up */
- printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size);
-}
-
-static void
-print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused)
-{
- uint8_t *walker = buf;
- uint32_t min;
-
- printf("Temperature History:\n");
- printf(" %-30s: %d C\n", "Current Temperature", *walker++);
- printf(" %-30s: %d C\n", "Reference Temperature", *walker++);
- printf(" %-30s: %d C\n", "Maximum Temperature", *walker++);
- printf(" %-30s: %d C\n", "Minimum Temperature", *walker++);
- min = le32dec(walker);
- walker += 4;
- printf(" %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60);
- min = le32dec(walker);
- walker += 4;
- printf(" %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, min % 60);
- min = le32dec(walker);
- walker += 4;
- printf(" %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60);
-}
-
-static void
-print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused)
-{
- uint8_t *walker = buf;
- uint64_t val;
-
- printf("SSD Performance Subpage Type %d:\n", res);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "Host Read Commands", val);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "Host Read Blocks", val);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "Host Read Commands Stalled", val);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "Host Write Commands", val);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "Host Write Blocks", val);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "Host Write Odd End Commands", val);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "Host Write Commands Stalled", val);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "NAND Read Commands", val);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "NAND Read Blocks", val);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "NAND Write Commands", val);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "NAND Write Blocks", val);
- val = le64dec(walker);
- walker += 8;
- printf(" %-30s: %ju\n", "NAND Read Before Writes", val);
-}
-
-static void
-print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused)
-{
- uint8_t *walker = buf;
-
- printf("Firmware Load Subpage:\n");
- printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker));
-}
-
-static void
-kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp)
-{
- size_t i;
-
- for (i = 0; i < nsp; i++, sp++) {
- if (sp->key == subtype) {
- sp->fn(buf, subtype, res, size);
- return;
- }
- }
- printf("No handler for page type %x\n", subtype);
-}
-
-static void
-print_hgst_info_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
-{
- uint8_t *walker, *end, *subpage;
- int pages;
- uint16_t len;
- uint8_t subtype, res;
-
- printf("HGST Extra Info Log\n");
- printf("===================\n");
-
- walker = buf;
- pages = *walker++;
- walker++;
- len = le16dec(walker);
- walker += 2;
- end = walker + len; /* Length is exclusive of this header */
-
- while (walker < end) {
- subpage = walker + 4;
- subtype = *walker++ & 0x3f; /* subtype */
- res = *walker++; /* Reserved */
- len = le16dec(walker);
- walker += len + 2; /* Length, not incl header */
- if (walker > end) {
- printf("Ooops! Off the end of the list\n");
- break;
- }
- kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage));
- }
-}
-
/*
* Table of log page printer / sizing.
*
- * This includes Intel specific pages that are widely implemented.
* Make sure you keep all the pages of one vendor together so -v help
* lists all the vendors pages.
*/
-static struct logpage_function {
- uint8_t log_page;
- const char *vendor;
- const char *name;
- print_fn_t print_fn;
- size_t size;
-} logfuncs[] = {
- {NVME_LOG_ERROR, NULL, "Drive Error Log",
- print_log_error, 0},
- {NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data",
- print_log_health, sizeof(struct nvme_health_information_page)},
- {NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information",
- print_log_firmware, sizeof(struct nvme_firmware_page)},
- {HGST_INFO_LOG, "hgst", "Detailed Health/SMART",
- print_hgst_info_log, DEFAULT_SIZE},
- {HGST_INFO_LOG, "wdc", "Detailed Health/SMART",
- print_hgst_info_log, DEFAULT_SIZE},
- {HGST_INFO_LOG, "wds", "Detailed Health/SMART",
- print_hgst_info_log, DEFAULT_SIZE},
- {INTEL_LOG_TEMP_STATS, "intel", "Temperature Stats",
- print_intel_temp_stats, sizeof(struct intel_log_temp_stats)},
- {INTEL_LOG_READ_LAT_LOG, "intel", "Read Latencies",
- print_intel_read_lat_log, DEFAULT_SIZE},
- {INTEL_LOG_WRITE_LAT_LOG, "intel", "Write Latencies",
- print_intel_write_lat_log, DEFAULT_SIZE},
- {INTEL_LOG_ADD_SMART, "intel", "Extra Health/SMART Data",
- print_intel_add_smart, DEFAULT_SIZE},
- {INTEL_LOG_ADD_SMART, "samsung", "Extra Health/SMART Data",
- print_intel_add_smart, DEFAULT_SIZE},
-
- {0, NULL, NULL, NULL, 0},
-};
-
-static void
-logpage_usage(void)
-{
- fprintf(stderr, "usage:\n");
- fprintf(stderr, LOGPAGE_USAGE);
- exit(1);
-}
+NVME_LOGPAGE(error,
+ NVME_LOG_ERROR, NULL, "Drive Error Log",
+ print_log_error, 0);
+NVME_LOGPAGE(healt,
+ NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data",
+ print_log_health, sizeof(struct nvme_health_information_page));
+NVME_LOGPAGE(fw,
+ NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information",
+ print_log_firmware, sizeof(struct nvme_firmware_page));
static void
logpage_help(void)
{
- struct logpage_function *f;
+ struct logpage_function **f;
const char *v;
fprintf(stderr, "\n");
fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name");
fprintf(stderr, "-------- ---------- ----------\n");
- for (f = logfuncs; f->log_page > 0; f++) {
- v = f->vendor == NULL ? "-" : f->vendor;
- fprintf(stderr, "0x%02x %-10s %s\n", f->log_page, v, f->name);
+ for (f = SET_BEGIN(logpage); f < SET_LIMIT(logpage); f++) {
+ v = (*f)->vendor == NULL ? "-" : (*f)->vendor;
+ fprintf(stderr, "0x%02x %-10s %s\n", (*f)->log_page, v, (*f)->name);
}
exit(1);
}
-void
-logpage(int argc, char *argv[])
+static void
+logpage(struct nvme_function *nf, int argc, char *argv[])
{
int fd;
int log_page = 0, pageflag = false;
@@ -920,7 +352,7 @@
uint32_t nsid, size;
void *buf;
const char *vendor = NULL;
- struct logpage_function *f;
+ struct logpage_function **f;
struct nvme_controller_data cdata;
print_fn_t print_fn;
uint8_t ns_smart;
@@ -940,7 +372,7 @@
fprintf(stderr,
"\"%s\" not valid log page id.\n",
optarg);
- logpage_usage();
+ usage(nf);
}
pageflag = true;
break;
@@ -957,12 +389,12 @@
if (!pageflag) {
printf("Missing page_id (-p).\n");
- logpage_usage();
+ usage(nf);
}
/* Check that a controller and/or namespace was specified. */
if (optind >= argc)
- logpage_usage();
+ usage(nf);
if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) {
ns_specified = true;
@@ -1006,14 +438,14 @@
* the page is vendor specific, don't match the print function
* unless the vendors match.
*/
- for (f = logfuncs; f->log_page > 0; f++) {
- if (f->vendor != NULL && vendor != NULL &&
- strcmp(f->vendor, vendor) != 0)
+ for (f = SET_BEGIN(logpage); f < SET_LIMIT(logpage); f++) {
+ if ((*f)->vendor != NULL && vendor != NULL &&
+ strcmp((*f)->vendor, vendor) != 0)
continue;
- if (log_page != f->log_page)
+ if (log_page != (*f)->log_page)
continue;
- print_fn = f->print_fn;
- size = f->size;
+ print_fn = (*f)->print_fn;
+ size = (*f)->size;
break;
}
}
@@ -1031,3 +463,5 @@
close(fd);
exit(0);
}
+
+NVME_COMMAND(top, logpage, logpage, LOGPAGE_USAGE);
Index: sbin/nvmecontrol/ns.c
===================================================================
--- sbin/nvmecontrol/ns.c
+++ sbin/nvmecontrol/ns.c
@@ -41,64 +41,34 @@
#include "nvmecontrol.h"
+SET_DECLARE(ns, struct nvme_function);
+
+#define NS_USAGE \
+ "ns (create|delete|attach|detach)\n"
+
/* handles NVME_OPC_NAMESPACE_MANAGEMENT and ATTACHMENT admin cmds */
#define NSCREATE_USAGE \
-" nvmecontrol ns create -s size [-c cap] [-f fmt] [-m mset] [-n nmic] [-p pi] [-l pil] nvmeN\n"
+ "ns create -s size [-c cap] [-f fmt] [-m mset] [-n nmic] [-p pi] [-l pil] nvmeN\n"
#define NSDELETE_USAGE \
-" nvmecontrol ns delete -n nsid nvmeN\n"
+ "ns delete -n nsid nvmeN\n"
#define NSATTACH_USAGE \
-" nvmecontrol ns attach -n nsid [-c ctrlrid] nvmeN \n"
+ "ns attach -n nsid [-c ctrlrid] nvmeN \n"
#define NSDETACH_USAGE \
-" nvmecontrol ns detach -n nsid [-c ctrlrid] nvmeN\n"
-
-void nscreate(int argc, char *argv[]);
-void nsdelete(int argc, char *argv[]);
-void nsattach(int argc, char *argv[]);
-void nsdetach(int argc, char *argv[]);
-
-static struct nvme_function ns_funcs[] = {
- {"create", nscreate, NSCREATE_USAGE},
- {"delete", nsdelete, NSDELETE_USAGE},
- {"attach", nsattach, NSATTACH_USAGE},
- {"detach", nsdetach, NSDETACH_USAGE},
- {NULL, NULL, NULL},
-};
-
-static void
-nscreate_usage(void)
-{
- fprintf(stderr, "usage:\n");
- fprintf(stderr, NSCREATE_USAGE);
- exit(1);
-}
-
-static void
-nsdelete_usage(void)
-{
- fprintf(stderr, "usage:\n");
- fprintf(stderr, NSDELETE_USAGE);
- exit(1);
-}
+ "ns detach -n nsid [-c ctrlrid] nvmeN\n"
-static void
-nsattach_usage(void)
-{
- fprintf(stderr, "usage:\n");
- fprintf(stderr, NSATTACH_USAGE);
- exit(1);
-}
+void nscreate(struct nvme_function *nf, int argc, char *argv[]);
+void nsdelete(struct nvme_function *nf, int argc, char *argv[]);
+void nsattach(struct nvme_function *nf, int argc, char *argv[]);
+void nsdetach(struct nvme_function *nf, int argc, char *argv[]);
-static void
-nsdetach_usage(void)
-{
- fprintf(stderr, "usage:\n");
- fprintf(stderr, NSDETACH_USAGE);
- exit(1);
-}
+NVME_COMMAND(ns, create, nscreate, NSCREATE_USAGE);
+NVME_COMMAND(ns, delete, nsdelete, NSDELETE_USAGE);
+NVME_COMMAND(ns, attach, nsattach, NSATTACH_USAGE);
+NVME_COMMAND(ns, detach, nsdetach, NSDETACH_USAGE);
struct ns_result_str {
uint16_t res;
@@ -140,7 +110,7 @@
* 0xb = Thin Provisioning Not supported
*/
void
-nscreate(int argc, char *argv[])
+nscreate(struct nvme_function *nf, int argc, char *argv[])
{
struct nvme_pt_command pt;
struct nvme_controller_data cd;
@@ -149,7 +119,7 @@
int ch, fd, result, lbaf = 0, mset = 0, nmic = -1, pi = 0, pil = 0;
if (optind >= argc)
- nscreate_usage();
+ usage(nf);
while ((ch = getopt(argc, argv, "s:c:f:m:n:p:l:")) != -1) {
switch (ch) {
@@ -175,17 +145,17 @@
pil = strtol(optarg, NULL, 0);
break;
default:
- nscreate_usage();
+ usage(nf);
}
}
if (optind >= argc)
- nscreate_usage();
+ usage(nf);
if (cap == -1)
cap = nsze;
if (nsze == -1 || cap == -1)
- nscreate_usage();
+ usage(nf);
open_dev(argv[optind], &fd, 1, 1);
read_controller_data(fd, &cd);
@@ -235,7 +205,7 @@
}
void
-nsdelete(int argc, char *argv[])
+nsdelete(struct nvme_function *nf, int argc, char *argv[])
{
struct nvme_pt_command pt;
struct nvme_controller_data cd;
@@ -243,7 +213,7 @@
char buf[2];
if (optind >= argc)
- nsdelete_usage();
+ usage(nf);
while ((ch = getopt(argc, argv, "n:")) != -1) {
switch ((char)ch) {
@@ -251,12 +221,12 @@
nsid = strtol(optarg, (char **)NULL, 0);
break;
default:
- nsdelete_usage();
+ usage(nf);
}
}
if (optind >= argc || nsid == -2)
- nsdelete_usage();
+ usage(nf);
open_dev(argv[optind], &fd, 1, 1);
read_controller_data(fd, &cd);
@@ -302,7 +272,7 @@
* 0x2 Invalid Field can occur if ctrlrid d.n.e in system.
*/
void
-nsattach(int argc, char *argv[])
+nsattach(struct nvme_function *nf, int argc, char *argv[])
{
struct nvme_pt_command pt;
struct nvme_controller_data cd;
@@ -311,7 +281,7 @@
uint16_t clist[2048];
if (optind >= argc)
- nsattach_usage();
+ usage(nf);
while ((ch = getopt(argc, argv, "n:c:")) != -1) {
switch (ch) {
@@ -322,15 +292,15 @@
ctrlrid = strtol(optarg, (char **)NULL, 0);
break;
default:
- nsattach_usage();
+ usage(nf);
}
}
if (optind >= argc)
- nsattach_usage();
+ usage(nf);
if (nsid == -1 )
- nsattach_usage();
+ usage(nf);
open_dev(argv[optind], &fd, 1, 1);
read_controller_data(fd, &cd);
@@ -381,7 +351,7 @@
}
void
-nsdetach(int argc, char *argv[])
+nsdetach(struct nvme_function *nf, int argc, char *argv[])
{
struct nvme_pt_command pt;
struct nvme_controller_data cd;
@@ -390,7 +360,7 @@
uint16_t clist[2048];
if (optind >= argc)
- nsdetach_usage();
+ usage(nf);
while ((ch = getopt(argc, argv, "n:c:")) != -1) {
switch (ch) {
@@ -401,15 +371,15 @@
ctrlrid = strtol(optarg, (char **)NULL, 0);
break;
default:
- nsdetach_usage();
+ usage(nf);
}
}
if (optind >= argc)
- nsdetach_usage();
+ usage(nf);
if (nsid == -1)
- nsdetach_usage();
+ usage(nf);
open_dev(argv[optind], &fd, 1, 1);
read_controller_data(fd, &cd);
@@ -466,9 +436,11 @@
exit(0);
}
-void
-ns(int argc, char *argv[])
+static void
+ns(struct nvme_function *nf __unused, int argc, char *argv[])
{
- dispatch(argc, argv, ns_funcs);
+ DISPATCH(argc, argv, ns);
}
+
+NVME_COMMAND(top, ns, ns, NS_USAGE);
Index: sbin/nvmecontrol/nvmecontrol.h
===================================================================
--- sbin/nvmecontrol/nvmecontrol.h
+++ sbin/nvmecontrol/nvmecontrol.h
@@ -31,9 +31,11 @@
#ifndef __NVMECONTROL_H__
#define __NVMECONTROL_H__
+#include <sys/linker_set.h>
#include <dev/nvme/nvme.h>
-typedef void (*nvme_fn_t)(int argc, char *argv[]);
+struct nvme_function;
+typedef void (*nvme_fn_t)(struct nvme_function *nf, int argc, char *argv[]);
struct nvme_function {
const char *name;
@@ -41,52 +43,44 @@
const char *usage;
};
-#define NVME_CTRLR_PREFIX "nvme"
-#define NVME_NS_PREFIX "ns"
-
-#define DEVLIST_USAGE \
-" nvmecontrol devlist\n"
-
-#define IDENTIFY_USAGE \
-" nvmecontrol identify [-x [-v]] <controller id|namespace id>\n"
-
-#define PERFTEST_USAGE \
-" nvmecontrol perftest <-n num_threads> <-o read|write>\n" \
-" <-s size_in_bytes> <-t time_in_seconds>\n" \
-" <-i intr|wait> [-f refthread] [-p]\n" \
-" <namespace id>\n"
-
-#define RESET_USAGE \
-" nvmecontrol reset <controller id>\n"
-
-#define LOGPAGE_USAGE \
-" nvmecontrol logpage <-p page_id> [-b] [-v vendor] [-x] <controller id|namespace id>\n" \
-
-#define FIRMWARE_USAGE \
-" nvmecontrol firmware [-s slot] [-f path_to_firmware] [-a] <controller id>\n"
+#define NVME_CMDSET(set, sym) DATA_SET(set, sym)
+#define NVME_COMMAND(set, nam, function, usage_str) \
+ static struct nvme_function function ## _nvme_cmd = \
+ { .name = #nam, .fn = function, .usage = usage_str }; \
+ NVME_CMDSET(set, function ## _nvme_cmd);
-#define FORMAT_USAGE \
-" nvmecontrol format [-f fmt] [-m mset] [-p pi] [-l pil] [-E] [-C] <controller id|namespace id>\n"
+typedef void (*print_fn_t)(const struct nvme_controller_data *cdata, void *buf, uint32_t size);
-#define POWER_USAGE \
-" nvmecontrol power [-l] [-p new-state [-w workload-hint]] <controller id>\n"
+struct logpage_function {
+ uint8_t log_page;
+ const char *vendor;
+ const char *name;
+ print_fn_t print_fn;
+ size_t size;
+};
-#define WDC_USAGE \
-" nvmecontrol wdc (cap-diag|drive-log|get-crash-dump|purge|purge-montior)\n"
+#define NVME_LOGPAGESET(sym) DATA_SET(logpage, sym)
+#define NVME_LOGPAGE(unique, lp, vend, nam, fn, sz) \
+ static struct logpage_function unique ## _lpf = { \
+ .log_page = lp, \
+ .vendor = vend, \
+ .name = nam, \
+ .print_fn = fn, \
+ .size = sz, \
+ } ; \
+ NVME_LOGPAGESET(unique ## _lpf);
+
+#define DEFAULT_SIZE (4096)
+struct kv_name
+{
+ uint32_t key;
+ const char *name;
+};
-#define NS_USAGE \
-" nvmecontrol ns (create|delete|attach|detach)\n"
+const char *kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key);
-void devlist(int argc, char *argv[]);
-void identify(int argc, char *argv[]);
-void perftest(int argc, char *argv[]);
-void reset(int argc, char *argv[]);
-void logpage(int argc, char *argv[]);
-void firmware(int argc, char *argv[]);
-void format(int argc, char *argv[]);
-void power(int argc, char *argv[]);
-void wdc(int argc, char *argv[]);
-void ns(int argc, char *argv[]);
+#define NVME_CTRLR_PREFIX "nvme"
+#define NVME_NS_PREFIX "ns"
int open_dev(const char *str, int *fd, int show_error, int exit_on_error);
void parse_ns_str(const char *ns_str, char *ctrlr_str, uint32_t *nsid);
@@ -95,8 +89,14 @@
void print_hex(void *data, uint32_t length);
void read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload,
uint32_t payload_size);
-void gen_usage(struct nvme_function *);
-void dispatch(int argc, char *argv[], struct nvme_function *f);
+void print_temp(uint16_t t);
+
+void usage(struct nvme_function *f);
+void dispatch_set(int argc, char *argv[], struct nvme_function **tbl,
+ struct nvme_function **tbl_limit);
+
+#define DISPATCH(argc, argv, set) \
+ dispatch_set(argc, argv, SET_BEGIN(set), SET_LIMIT(set))
/* Utility Routines */
/*
Index: sbin/nvmecontrol/nvmecontrol.c
===================================================================
--- sbin/nvmecontrol/nvmecontrol.c
+++ sbin/nvmecontrol/nvmecontrol.c
@@ -47,51 +47,73 @@
#include "nvmecontrol.h"
+SET_DECLARE(top, struct nvme_function);
-static struct nvme_function funcs[] = {
- {"devlist", devlist, DEVLIST_USAGE},
- {"identify", identify, IDENTIFY_USAGE},
- {"perftest", perftest, PERFTEST_USAGE},
- {"reset", reset, RESET_USAGE},
- {"logpage", logpage, LOGPAGE_USAGE},
- {"firmware", firmware, FIRMWARE_USAGE},
- {"format", format, FORMAT_USAGE},
- {"power", power, POWER_USAGE},
- {"wdc", wdc, WDC_USAGE},
- {"ns", ns, NS_USAGE},
- {NULL, NULL, NULL},
-};
+static void
+print_usage(struct nvme_function *f)
+{
+ const char *cp;
+ char ch;
+ bool need_prefix = true;
+
+ cp = f->usage;
+ while (*cp) {
+ ch = *cp++;
+ if (need_prefix) {
+ if (ch != ' ')
+ fputs(" nvmecontrol ", stderr);
+ else
+ fputs(" ", stderr);
+ }
+ fputc(ch, stderr);
+ need_prefix = (ch == '\n');
+ }
+ if (!need_prefix)
+ fputc('\n', stderr);
+}
-void
-gen_usage(struct nvme_function *f)
+static void
+gen_usage_set(struct nvme_function **f, struct nvme_function **flimit)
{
fprintf(stderr, "usage:\n");
- while (f->name != NULL) {
- fprintf(stderr, "%s", f->usage);
+ while (f < flimit) {
+ print_usage(*f);
f++;
}
exit(1);
}
void
-dispatch(int argc, char *argv[], struct nvme_function *tbl)
+usage(struct nvme_function *f)
+{
+
+ fprintf(stderr, "usage:\n");
+ print_usage(f);
+ exit(1);
+}
+
+void
+dispatch_set(int argc, char *argv[], struct nvme_function **tbl,
+ struct nvme_function **tbl_limit)
{
- struct nvme_function *f = tbl;
+ struct nvme_function **f = tbl;
if (argv[1] == NULL) {
- gen_usage(tbl);
+ gen_usage_set(tbl, tbl_limit);
return;
}
- while (f->name != NULL) {
- if (strcmp(argv[1], f->name) == 0)
- f->fn(argc-1, &argv[1]);
+ while (f < tbl_limit) {
+ if (strcmp(argv[1], (*f)->name) == 0) {
+ (*f)->fn(*f, argc-1, &argv[1]);
+ return;
+ }
f++;
}
fprintf(stderr, "Unknown command: %s\n", argv[1]);
- gen_usage(tbl);
+ gen_usage_set(tbl, tbl_limit);
}
static void
@@ -243,9 +265,9 @@
{
if (argc < 2)
- gen_usage(funcs);
+ gen_usage_set(SET_BEGIN(top), SET_LIMIT(top));
- dispatch(argc, argv, funcs);
+ DISPATCH(argc, argv, top);
return (0);
}
Index: sbin/nvmecontrol/perftest.c
===================================================================
--- sbin/nvmecontrol/perftest.c
+++ sbin/nvmecontrol/perftest.c
@@ -45,6 +45,12 @@
#include "nvmecontrol.h"
+#define PERFTEST_USAGE \
+ "perftest <-n num_threads> <-o read|write>\n" \
+ " <-s size_in_bytes> <-t time_in_seconds>\n" \
+ " <-i intr|wait> [-f refthread] [-p]\n" \
+ " <namespace id>\n"
+
static void
print_perftest(struct nvme_io_test *io_test, bool perthread)
{
@@ -69,15 +75,7 @@
}
static void
-perftest_usage(void)
-{
- fprintf(stderr, "usage:\n");
- fprintf(stderr, PERFTEST_USAGE);
- exit(1);
-}
-
-void
-perftest(int argc, char *argv[])
+perftest(struct nvme_function *nf, int argc, char *argv[])
{
struct nvme_io_test io_test;
int fd;
@@ -112,13 +110,13 @@
fprintf(stderr,
"\"%s\" not valid number of threads.\n",
optarg);
- perftest_usage();
+ usage(nf);
} else if (io_test.num_threads == 0 ||
io_test.num_threads > 128) {
fprintf(stderr,
"\"%s\" not valid number of threads.\n",
optarg);
- perftest_usage();
+ usage(nf);
}
break;
case 'o':
@@ -131,7 +129,7 @@
else {
fprintf(stderr, "\"%s\" not valid opcode.\n",
optarg);
- perftest_usage();
+ usage(nf);
}
break;
case 'p':
@@ -149,7 +147,7 @@
} else {
fprintf(stderr, "\"%s\" not valid size.\n",
optarg);
- perftest_usage();
+ usage(nf);
}
break;
case 't':
@@ -159,14 +157,15 @@
fprintf(stderr,
"\"%s\" not valid time duration.\n",
optarg);
- perftest_usage();
+ usage(nf);
}
break;
}
}
if (!nflag || !oflag || !sflag || !tflag || optind >= argc)
- perftest_usage();
+ usage(nf);
+
open_dev(argv[optind], &fd, 1, 1);
if (ioctl(fd, ioctl_cmd, &io_test) < 0)
@@ -176,3 +175,5 @@
print_perftest(&io_test, perthread);
exit(0);
}
+
+NVME_COMMAND(top, perftest, perftest, PERFTEST_USAGE);
Index: sbin/nvmecontrol/power.c
===================================================================
--- sbin/nvmecontrol/power.c
+++ sbin/nvmecontrol/power.c
@@ -44,13 +44,8 @@
_Static_assert(sizeof(struct nvme_power_state) == 256 / NBBY,
"nvme_power_state size wrong");
-static void
-power_usage(void)
-{
- fprintf(stderr, "usage:\n");
- fprintf(stderr, POWER_USAGE);
- exit(1);
-}
+#define POWER_USAGE \
+ "power [-l] [-p new-state [-w workload-hint]] <controller id>\n"
static void
power_list_one(int i, struct nvme_power_state *nps)
@@ -133,8 +128,8 @@
printf("Current Power Mode is %d\n", pt.cpl.cdw0);
}
-void
-power(int argc, char *argv[])
+static void
+power(struct nvme_function *nf, int argc, char *argv[])
{
struct nvme_controller_data cdata;
int ch, listflag = 0, powerflag = 0, power_val = 0, fd;
@@ -151,28 +146,28 @@
power_val = strtol(optarg, &end, 0);
if (*end != '\0') {
fprintf(stderr, "Invalid power state number: %s\n", optarg);
- power_usage();
+ usage(nf);
}
break;
case 'w':
workload = strtol(optarg, &end, 0);
if (*end != '\0') {
fprintf(stderr, "Invalid workload hint: %s\n", optarg);
- power_usage();
+ usage(nf);
}
break;
default:
- power_usage();
+ usage(nf);
}
}
/* Check that a controller was specified. */
if (optind >= argc)
- power_usage();
+ usage(nf);
if (listflag && powerflag) {
fprintf(stderr, "Can't set power and list power states\n");
- power_usage();
+ usage(nf);
}
open_dev(argv[optind], &fd, 1, 1);
@@ -193,3 +188,5 @@
close(fd);
exit(0);
}
+
+NVME_COMMAND(top, power, power, POWER_USAGE);
Index: sbin/nvmecontrol/reset.c
===================================================================
--- sbin/nvmecontrol/reset.c
+++ sbin/nvmecontrol/reset.c
@@ -41,29 +41,24 @@
#include "nvmecontrol.h"
-static void
-reset_usage(void)
-{
- fprintf(stderr, "usage:\n");
- fprintf(stderr, RESET_USAGE);
- exit(1);
-}
+#define RESET_USAGE \
+ "reset <controller id>\n"
-void
-reset(int argc, char *argv[])
+static void
+reset(struct nvme_function *nf, int argc, char *argv[])
{
int ch, fd;
while ((ch = getopt(argc, argv, "")) != -1) {
switch ((char)ch) {
default:
- reset_usage();
+ usage(nf);
}
}
/* Check that a controller was specified. */
if (optind >= argc)
- reset_usage();
+ usage(nf);
open_dev(argv[optind], &fd, 1, 1);
if (ioctl(fd, NVME_RESET_CONTROLLER) < 0)
@@ -71,3 +66,5 @@
exit(0);
}
+
+NVME_COMMAND(top, reset, reset, RESET_USAGE);
Index: sbin/nvmecontrol/wdc.c
===================================================================
--- sbin/nvmecontrol/wdc.c
+++ sbin/nvmecontrol/wdc.c
@@ -42,19 +42,21 @@
#include "nvmecontrol.h"
+#define WDC_USAGE \
+ "wdc (cap-diag)\n"
+
+SET_DECLARE(wdc, struct nvme_function);
+
#define WDC_NVME_TOC_SIZE 8
#define WDC_NVME_CAP_DIAG_OPCODE 0xe6
#define WDC_NVME_CAP_DIAG_CMD 0x0000
-static void wdc_cap_diag(int argc, char *argv[]);
+static void wdc_cap_diag(struct nvme_function *nf, int argc, char *argv[]);
-#define WDC_CAP_DIAG_USAGE "\tnvmecontrol wdc cap-diag [-o path-template]\n"
+#define WDC_CAP_DIAG_USAGE "wdc cap-diag [-o path-template]\n"
-static struct nvme_function wdc_funcs[] = {
- {"cap-diag", wdc_cap_diag, WDC_CAP_DIAG_USAGE},
- {NULL, NULL, NULL},
-};
+NVME_COMMAND(wdc, cap-diag, wdc_cap_diag, WDC_CAP_DIAG_USAGE);
static void
wdc_append_serial_name(int fd, char *buf, size_t len, const char *suffix)
@@ -152,15 +154,7 @@
}
static void
-wdc_cap_diag_usage(void)
-{
- fprintf(stderr, "usage:\n");
- fprintf(stderr, WDC_CAP_DIAG_USAGE);
- exit(1);
-}
-
-static void
-wdc_cap_diag(int argc, char *argv[])
+wdc_cap_diag(struct nvme_function *nf, int argc, char *argv[])
{
char path_tmpl[MAXPATHLEN];
int ch, fd;
@@ -172,12 +166,12 @@
strlcpy(path_tmpl, optarg, MAXPATHLEN);
break;
default:
- wdc_cap_diag_usage();
+ usage(nf);
}
}
/* Check that a controller was specified. */
if (optind >= argc)
- wdc_cap_diag_usage();
+ usage(nf);
open_dev(argv[optind], &fd, 1, 1);
wdc_do_dump(fd, path_tmpl, "cap_diag", WDC_NVME_CAP_DIAG_OPCODE,
@@ -188,9 +182,416 @@
exit(1);
}
-void
-wdc(int argc, char *argv[])
+static void
+wdc(struct nvme_function *nf __unused, int argc, char *argv[])
+{
+
+ DISPATCH(argc, argv, wdc);
+}
+
+/*
+ * HGST's 0xc1 page. This is a grab bag of additional data. Please see
+ * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf
+ * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf
+ * Appendix A for details
+ */
+
+typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
+
+struct subpage_print
+{
+ uint16_t key;
+ subprint_fn_t fn;
+};
+
+static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
+static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
+static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
+static void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
+static void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
+static void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
+static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
+static void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
+static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
+static void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
+
+static struct subpage_print hgst_subpage[] = {
+ { 0x02, print_hgst_info_write_errors },
+ { 0x03, print_hgst_info_read_errors },
+ { 0x05, print_hgst_info_verify_errors },
+ { 0x10, print_hgst_info_self_test },
+ { 0x15, print_hgst_info_background_scan },
+ { 0x30, print_hgst_info_erase_errors },
+ { 0x31, print_hgst_info_erase_counts },
+ { 0x32, print_hgst_info_temp_history },
+ { 0x37, print_hgst_info_ssd_perf },
+ { 0x38, print_hgst_info_firmware_load },
+};
+
+/* Print a subpage that is basically just key value pairs */
+static void
+print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size,
+ const struct kv_name *kv, size_t kv_count)
+{
+ uint8_t *wsp, *esp;
+ uint16_t ptype;
+ uint8_t plen;
+ uint64_t param;
+ int i;
+
+ wsp = buf;
+ esp = wsp + size;
+ while (wsp < esp) {
+ ptype = le16dec(wsp);
+ wsp += 2;
+ wsp++; /* Flags, just ignore */
+ plen = *wsp++;
+ param = 0;
+ for (i = 0; i < plen; i++)
+ param |= (uint64_t)*wsp++ << (i * 8);
+ printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param);
+ }
+}
+
+static void
+print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
+{
+ static struct kv_name kv[] =
+ {
+ { 0x0000, "Corrected Without Delay" },
+ { 0x0001, "Corrected Maybe Delayed" },
+ { 0x0002, "Re-Writes" },
+ { 0x0003, "Errors Corrected" },
+ { 0x0004, "Correct Algorithm Used" },
+ { 0x0005, "Bytes Processed" },
+ { 0x0006, "Uncorrected Errors" },
+ { 0x8000, "Flash Write Commands" },
+ { 0x8001, "HGST Special" },
+ };
+
+ printf("Write Errors Subpage:\n");
+ print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
+}
+
+static void
+print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
+{
+ static struct kv_name kv[] =
+ {
+ { 0x0000, "Corrected Without Delay" },
+ { 0x0001, "Corrected Maybe Delayed" },
+ { 0x0002, "Re-Reads" },
+ { 0x0003, "Errors Corrected" },
+ { 0x0004, "Correct Algorithm Used" },
+ { 0x0005, "Bytes Processed" },
+ { 0x0006, "Uncorrected Errors" },
+ { 0x8000, "Flash Read Commands" },
+ { 0x8001, "XOR Recovered" },
+ { 0x8002, "Total Corrected Bits" },
+ };
+
+ printf("Read Errors Subpage:\n");
+ print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
+}
+
+static void
+print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
+{
+ static struct kv_name kv[] =
+ {
+ { 0x0000, "Corrected Without Delay" },
+ { 0x0001, "Corrected Maybe Delayed" },
+ { 0x0002, "Re-Reads" },
+ { 0x0003, "Errors Corrected" },
+ { 0x0004, "Correct Algorithm Used" },
+ { 0x0005, "Bytes Processed" },
+ { 0x0006, "Uncorrected Errors" },
+ { 0x8000, "Commands Processed" },
+ };
+
+ printf("Verify Errors Subpage:\n");
+ print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
+}
+
+static void
+print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
+{
+ size_t i;
+ uint8_t *walker = buf;
+ uint16_t code, hrs;
+ uint32_t lba;
+
+ printf("Self Test Subpage:\n");
+ for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */
+ code = le16dec(walker);
+ walker += 2;
+ walker++; /* Ignore fixed flags */
+ if (*walker == 0) /* Last entry is zero length */
+ break;
+ if (*walker++ != 0x10) {
+ printf("Bad length for self test report\n");
+ return;
+ }
+ printf(" %-30s: %d\n", "Recent Test", code);
+ printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf);
+ printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7);
+ walker++;
+ printf(" %-28s: %#x\n", "Self-Test Number", *walker++);
+ hrs = le16dec(walker);
+ walker += 2;
+ lba = le32dec(walker);
+ walker += 4;
+ printf(" %-28s: %u\n", "Total Power On Hrs", hrs);
+ printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba);
+ printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf);
+ printf(" %-28s: %#x\n", "Additional Sense Code", *walker++);
+ printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++);
+ printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++);
+ }
+}
+
+static void
+print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
+{
+ uint8_t *walker = buf;
+ uint8_t status;
+ uint16_t code, nscan, progress;
+ uint32_t pom, nand;
+
+ printf("Background Media Scan Subpage:\n");
+ /* Decode the header */
+ code = le16dec(walker);
+ walker += 2;
+ walker++; /* Ignore fixed flags */
+ if (*walker++ != 0x10) {
+ printf("Bad length for background scan header\n");
+ return;
+ }
+ if (code != 0) {
+ printf("Expceted code 0, found code %#x\n", code);
+ return;
+ }
+ pom = le32dec(walker);
+ walker += 4;
+ walker++; /* Reserved */
+ status = *walker++;
+ nscan = le16dec(walker);
+ walker += 2;
+ progress = le16dec(walker);
+ walker += 2;
+ walker += 6; /* Reserved */
+ printf(" %-30s: %d\n", "Power On Minutes", pom);
+ printf(" %-30s: %x (%s)\n", "BMS Status", status,
+ status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown")));
+ printf(" %-30s: %d\n", "Number of BMS", nscan);
+ printf(" %-30s: %d\n", "Progress Current BMS", progress);
+ /* Report retirements */
+ if (walker - (uint8_t *)buf != 20) {
+ printf("Coding error, offset not 20\n");
+ return;
+ }
+ size -= 20;
+ printf(" %-30s: %d\n", "BMS retirements", size / 0x18);
+ while (size > 0) {
+ code = le16dec(walker);
+ walker += 2;
+ walker++;
+ if (*walker++ != 0x14) {
+ printf("Bad length parameter\n");
+ return;
+ }
+ pom = le32dec(walker);
+ walker += 4;
+ /*
+ * Spec sheet says the following are hard coded, if true, just
+ * print the NAND retirement.
+ */
+ if (walker[0] == 0x41 &&
+ walker[1] == 0x0b &&
+ walker[2] == 0x01 &&
+ walker[3] == 0x00 &&
+ walker[4] == 0x00 &&
+ walker[5] == 0x00 &&
+ walker[6] == 0x00 &&
+ walker[7] == 0x00) {
+ walker += 8;
+ walker += 4; /* Skip reserved */
+ nand = le32dec(walker);
+ walker += 4;
+ printf(" %-30s: %d\n", "Retirement number", code);
+ printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand);
+ } else {
+ printf("Parameter %#x entry corrupt\n", code);
+ walker += 16;
+ }
+ }
+}
+
+static void
+print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
{
+ static struct kv_name kv[] =
+ {
+ { 0x0000, "Corrected Without Delay" },
+ { 0x0001, "Corrected Maybe Delayed" },
+ { 0x0002, "Re-Erase" },
+ { 0x0003, "Errors Corrected" },
+ { 0x0004, "Correct Algorithm Used" },
+ { 0x0005, "Bytes Processed" },
+ { 0x0006, "Uncorrected Errors" },
+ { 0x8000, "Flash Erase Commands" },
+ { 0x8001, "Mfg Defect Count" },
+ { 0x8002, "Grown Defect Count" },
+ { 0x8003, "Erase Count -- User" },
+ { 0x8004, "Erase Count -- System" },
+ };
- dispatch(argc, argv, wdc_funcs);
+ printf("Erase Errors Subpage:\n");
+ print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
}
+
+static void
+print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
+{
+ /* My drive doesn't export this -- so not coding up */
+ printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size);
+}
+
+static void
+print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused)
+{
+ uint8_t *walker = buf;
+ uint32_t min;
+
+ printf("Temperature History:\n");
+ printf(" %-30s: %d C\n", "Current Temperature", *walker++);
+ printf(" %-30s: %d C\n", "Reference Temperature", *walker++);
+ printf(" %-30s: %d C\n", "Maximum Temperature", *walker++);
+ printf(" %-30s: %d C\n", "Minimum Temperature", *walker++);
+ min = le32dec(walker);
+ walker += 4;
+ printf(" %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60);
+ min = le32dec(walker);
+ walker += 4;
+ printf(" %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, min % 60);
+ min = le32dec(walker);
+ walker += 4;
+ printf(" %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60);
+}
+
+static void
+print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused)
+{
+ uint8_t *walker = buf;
+ uint64_t val;
+
+ printf("SSD Performance Subpage Type %d:\n", res);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "Host Read Commands", val);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "Host Read Blocks", val);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "Host Read Commands Stalled", val);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "Host Write Commands", val);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "Host Write Blocks", val);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "Host Write Odd End Commands", val);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "Host Write Commands Stalled", val);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "NAND Read Commands", val);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "NAND Read Blocks", val);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "NAND Write Commands", val);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "NAND Write Blocks", val);
+ val = le64dec(walker);
+ walker += 8;
+ printf(" %-30s: %ju\n", "NAND Read Before Writes", val);
+}
+
+static void
+print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused)
+{
+ uint8_t *walker = buf;
+
+ printf("Firmware Load Subpage:\n");
+ printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker));
+}
+
+static void
+kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp)
+{
+ size_t i;
+
+ for (i = 0; i < nsp; i++, sp++) {
+ if (sp->key == subtype) {
+ sp->fn(buf, subtype, res, size);
+ return;
+ }
+ }
+ printf("No handler for page type %x\n", subtype);
+}
+
+static void
+print_hgst_info_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
+{
+ uint8_t *walker, *end, *subpage;
+ int pages;
+ uint16_t len;
+ uint8_t subtype, res;
+
+ printf("HGST Extra Info Log\n");
+ printf("===================\n");
+
+ walker = buf;
+ pages = *walker++;
+ walker++;
+ len = le16dec(walker);
+ walker += 2;
+ end = walker + len; /* Length is exclusive of this header */
+
+ while (walker < end) {
+ subpage = walker + 4;
+ subtype = *walker++ & 0x3f; /* subtype */
+ res = *walker++; /* Reserved */
+ len = le16dec(walker);
+ walker += len + 2; /* Length, not incl header */
+ if (walker > end) {
+ printf("Ooops! Off the end of the list\n");
+ break;
+ }
+ kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage));
+ }
+}
+
+NVME_LOGPAGE(hgst_info,
+ HGST_INFO_LOG, "hgst", "Detailed Health/SMART",
+ print_hgst_info_log, DEFAULT_SIZE);
+NVME_LOGPAGE(wdc_info,
+ HGST_INFO_LOG, "wdc", "Detailed Health/SMART",
+ print_hgst_info_log, DEFAULT_SIZE);
+NVME_COMMAND(top, wdc, wdc, WDC_USAGE);

File Metadata

Mime Type
text/plain
Expires
Sat, Oct 31, 12:38 PM (5 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41263295
Default Alt Text
D18403.1793450297.diff (66 KB)

Event Timeline