Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F176832087
D18403.1793450297.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
66 KB
Referenced Files
None
Subscribers
None
D18403.1793450297.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D18403: Move nvmecontrol to using linker sets
Attached
Detach File
Event Timeline