nvmectl(8): sync with FreeBSD HEAD r316105.

- Expand the SMART / Health Information Log Page (Page 02) printout based on
   NVM Express 1.2.1 Standard.
 - Implement Intel-specific log pages.
 - Implement HGST-specific log pages.
 - Implement wdc-specific nvme control options.
 - Add the ability to dump log pages directly in binary to stdout.
This commit is contained in:
nonaka 2017-04-29 00:06:40 +00:00
parent d6c1a4862f
commit d003492f75
7 changed files with 860 additions and 113 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.2 2017/02/13 11:16:46 nonaka Exp $
# $NetBSD: Makefile,v 1.3 2017/04/29 00:06:40 nonaka Exp $
.include <bsd.own.mk>
@ -11,6 +11,7 @@ SRCS+= logpage.c
SRCS+= perftest.c
SRCS+= power.c
SRCS+= reset.c
SRCS+= wdc.c
SRCS+= bignum.c
SRCS+= humanize_bignum.c
MAN= nvmectl.8

View File

@ -1,4 +1,4 @@
/* $NetBSD: firmware.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $ */
/* $NetBSD: firmware.c,v 1.2 2017/04/29 00:06:40 nonaka Exp $ */
/*-
* Copyright (c) 2013 EMC Corp.
@ -31,9 +31,9 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: firmware.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $");
__RCSID("$NetBSD: firmware.c,v 1.2 2017/04/29 00:06:40 nonaka Exp $");
#if 0
__FBSDID("$FreeBSD: head/sbin/nvmecontrol/firmware.c 258071 2013-11-12 21:14:19Z jimharris $");
__FBSDID("$FreeBSD: head/sbin/nvmecontrol/firmware.c 313188 2017-02-04 05:52:50Z imp $");
#endif
#endif
@ -121,7 +121,7 @@ update_firmware(int fd, uint8_t *payload, int32_t payload_size)
off = 0;
resid = payload_size;
if ((chunk = malloc(NVME_MAX_XFER_SIZE)) == NULL)
if ((chunk = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE)) == NULL)
errx(1, "unable to malloc %d bytes", NVME_MAX_XFER_SIZE);
while (resid > 0) {

View File

@ -1,4 +1,4 @@
/* $NetBSD: logpage.c,v 1.3 2017/02/13 11:16:46 nonaka Exp $ */
/* $NetBSD: logpage.c,v 1.4 2017/04/29 00:06:40 nonaka Exp $ */
/*-
* Copyright (c) 2013 EMC Corp.
@ -31,14 +31,15 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: logpage.c,v 1.3 2017/02/13 11:16:46 nonaka Exp $");
__RCSID("$NetBSD: logpage.c,v 1.4 2017/04/29 00:06:40 nonaka Exp $");
#if 0
__FBSDID("$FreeBSD: head/sbin/nvmecontrol/logpage.c 285796 2015-07-22 16:10:29Z jimharris $");
__FBSDID("$FreeBSD: head/sbin/nvmecontrol/logpage.c 314230 2017-02-25 00:09:16Z imp $");
#endif
#endif
#include <sys/param.h>
#include <sys/ioccom.h>
#include <sys/endian.h>
#include <ctype.h>
#include <err.h>
@ -58,6 +59,39 @@ __FBSDID("$FreeBSD: head/sbin/nvmecontrol/logpage.c 285796 2015-07-22 16:10:29Z
typedef void (*print_fn_t)(void *buf, uint32_t size);
struct kv_name {
uint32_t key;
const char *name;
};
static const char *
kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
{
static char bad[32];
size_t i;
for (i = 0; i < kv_count; i++, kv++)
if (kv->key == key)
return kv->name;
snprintf(bad, sizeof(bad), "Attribute %#x", key);
return bad;
}
static void
print_bin(void *data, uint32_t length)
{
write(STDOUT_FILENO, data, length);
}
/* "Missing" from endian.h */
static __inline uint64_t
le48dec(const void *pp)
{
uint8_t const *p = (uint8_t const *)pp;
return (((uint64_t)le16dec(p + 4) << 32) | le32dec(p));
}
static void *
get_log_buffer(uint32_t size)
{
@ -177,11 +211,18 @@ print_bignum(const char *title, uint64_t v[2], const char *suffix)
printf("%-31s %s\n", title, buf);
}
static 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);
}
static void
print_log_health(void *buf, uint32_t size __unused)
{
struct nvme_health_information_page *health = buf;
float composite_temperature = health->composite_temperature;
int i;
printf("SMART/Health Information Log\n");
printf("============================\n");
@ -203,10 +244,8 @@ print_log_health(void *buf, uint32_t size __unused)
printf(" Volatile memory backup: %d\n",
(uint8_t)__SHIFTOUT(health->critical_warning,
NVME_HEALTH_PAGE_CW_VOLATILE_MEMORY_BACKUP));
printf("Temperature: %u K, %2.2f C, %3.2f F\n",
health->composite_temperature,
composite_temperature - (float)273.15,
(composite_temperature * (float)9/5) - (float)459.67);
printf("Temperature: ");
print_temp(health->composite_temperature);
printf("Available spare: %u\n",
health->available_spare);
printf("Available spare threshold: %u\n",
@ -214,26 +253,28 @@ print_log_health(void *buf, uint32_t size __unused)
printf("Percentage used: %u\n",
health->percentage_used);
print_bignum("Data units (512 byte) read:",
health->data_units_read, "");
print_bignum("Data units (512 byte) written:",
health->data_units_written, "");
print_bignum("Host read commands:",
health->host_read_commands, "");
print_bignum("Host write commands:",
health->host_write_commands, "");
print_bignum("Controller busy time (minutes):",
health->controller_busy_time, "");
print_bignum("Power cycles:",
health->power_cycles, "");
print_bignum("Power on hours:",
health->power_on_hours, "");
print_bignum("Unsafe shutdowns:",
health->unsafe_shutdowns, "");
print_bignum("Media errors:",
health->media_errors, "");
print_bignum("Data units (512 byte) read:", health->data_units_read, "");
print_bignum("Data units (512 byte) written:", health->data_units_written,
"");
print_bignum("Host read commands:", health->host_read_commands, "");
print_bignum("Host write commands:", health->host_write_commands, "");
print_bignum("Controller busy time (minutes):", health->controller_busy_time,
"");
print_bignum("Power cycles:", health->power_cycles, "");
print_bignum("Power on hours:", health->power_on_hours, "");
print_bignum("Unsafe shutdowns:", health->unsafe_shutdowns, "");
print_bignum("Media errors:", health->media_errors, "");
print_bignum("No. error info log entries:",
health->num_error_info_log_entries, "");
printf("Warning Temp Composite Time: %d\n", health->warning_temp_time);
printf("Error Temp Composite Time: %d\n", health->error_temp_time);
for (i = 0; i < 7; i++) {
if (health->temp_sensor[i] == 0)
continue;
printf("Temperature Sensor %d: ", i + 1);
print_temp(health->temp_sensor[i]);
}
}
static void
@ -265,14 +306,593 @@ print_log_firmware(void *buf, uint32_t size __unused)
}
}
/*
* 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(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(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(void *buf, uint32_t size)
{
printf("Intel Read Latency Log\n");
printf("======================\n");
print_intel_read_write_lat_log(buf, size);
}
static void
print_intel_write_lat_log(void *buf, uint32_t size)
{
printf("Intel Write Latency Log\n");
printf("=======================\n");
print_intel_read_write_lat_log(buf, size);
}
/*
* Table 19. 5.4 SMART Attributes.
* Samsung also implements this and some extra data not documented.
*/
static void
print_intel_add_smart(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, __arraycount(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 *, uint16_t, uint8_t, uint32_t);
static void print_hgst_info_read_errors(void *, uint16_t, uint8_t, uint32_t);
static void print_hgst_info_verify_errors(void *, uint16_t, uint8_t, uint32_t);
static void print_hgst_info_self_test(void *, uint16_t, uint8_t, uint32_t);
static void print_hgst_info_background_scan(void *, uint16_t, uint8_t, uint32_t);
static void print_hgst_info_erase_errors(void *, uint16_t, uint8_t, uint32_t);
static void print_hgst_info_erase_counts(void *, uint16_t, uint8_t, uint32_t);
static void print_hgst_info_temp_history(void *, uint16_t, uint8_t, uint32_t);
static void print_hgst_info_ssd_perf(void *, uint16_t, uint8_t, uint32_t);
static void print_hgst_info_firmware_load(void *, uint16_t, uint8_t, uint32_t);
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 const 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, __arraycount(kv));
}
static void
print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused,
uint32_t size)
{
static const 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, __arraycount(kv));
}
static void
print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused,
uint32_t size)
{
static const 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, __arraycount(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 const 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, __arraycount(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(void *buf, uint32_t size __unused)
{
uint8_t *walker, *end, *subpage;
int pages __unused;
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,
__arraycount(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;
print_fn_t fn;
const char *vendor;
const char *name;
print_fn_t print_fn;
size_t size;
} logfuncs[] = {
{NVME_LOG_ERROR, print_log_error },
{NVME_LOG_HEALTH_INFORMATION, print_log_health },
{NVME_LOG_FIRMWARE_SLOT, print_log_firmware },
{0, NULL },
{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, "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},
};
__dead static void
@ -283,24 +903,48 @@ logpage_usage(void)
exit(1);
}
__dead static void
logpage_help(void)
{
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);
}
exit(1);
}
void
logpage(int argc, char *argv[])
{
int fd, nsid;
int log_page = 0, pageflag = false;
int hexflag = false, ns_specified;
int binflag = false, hexflag = false, ns_specified;
int ch;
char *p;
char cname[64];
uint32_t size;
void *buf;
const char *vendor = NULL;
struct logpage_function *f;
struct nvm_identify_controller cdata;
print_fn_t print_fn;
while ((ch = getopt(argc, argv, "p:x")) != -1) {
while ((ch = getopt(argc, argv, "bp:xv:")) != -1) {
switch (ch) {
case 'b':
binflag = true;
break;
case 'p':
if (strcmp(optarg, "help") == 0)
logpage_help();
/* TODO: Add human-readable ASCII page IDs */
log_page = strtol(optarg, &p, 0);
if (p != NULL && *p != '\0') {
@ -308,20 +952,17 @@ logpage(int argc, char *argv[])
"\"%s\" not valid log page id.\n",
optarg);
logpage_usage();
/* TODO: Define valid log page id ranges in nvme.h? */
} else if (log_page == 0 ||
(log_page >= 0x04 && log_page <= 0x7F) ||
(log_page >= 0x80 && log_page <= 0xBF)) {
fprintf(stderr,
"\"%s\" not valid log page id.\n",
optarg);
logpage_usage();
}
pageflag = true;
break;
case 'x':
hexflag = true;
break;
case 'v':
if (strcmp(optarg, "help") == 0)
logpage_help();
vendor = optarg;
break;
}
}
@ -362,39 +1003,35 @@ logpage(int argc, char *argv[])
}
print_fn = print_hex;
if (!hexflag) {
size = DEFAULT_SIZE;
if (binflag)
print_fn = print_bin;
if (!binflag && !hexflag) {
/*
* See if there is a pretty print function for the
* specified log page. If one isn't found, we
* just revert to the default (print_hex).
* See if there is a pretty print function for the specified log
* page. If one isn't found, we just revert to the default
* (print_hex). If there was a vendor specified bt the user, and
* the page is vendor specific, don't match the print function
* unless the vendors match.
*/
f = logfuncs;
while (f->log_page > 0) {
if (log_page == f->log_page) {
print_fn = f->fn;
break;
}
f++;
for (f = logfuncs; f->log_page > 0; f++) {
if (f->vendor != NULL && vendor != NULL &&
strcmp(f->vendor, vendor) != 0)
continue;
if (log_page != f->log_page)
continue;
print_fn = f->print_fn;
size = f->size;
break;
}
}
/* Read the log page */
switch (log_page) {
case NVME_LOG_ERROR:
if (log_page == NVME_LOG_ERROR) {
size = sizeof(struct nvme_error_information_entry);
size *= (cdata.elpe + 1);
break;
case NVME_LOG_HEALTH_INFORMATION:
size = sizeof(struct nvme_health_information_page);
break;
case NVME_LOG_FIRMWARE_SLOT:
size = sizeof(struct nvme_firmware_page);
break;
default:
size = DEFAULT_SIZE;
break;
}
/* Read the log page */
buf = get_log_buffer(size);
read_logpage(fd, log_page, nsid, buf, size);
print_fn(buf, size);

View File

@ -1,4 +1,4 @@
/* $NetBSD: nvme.h,v 1.1 2016/06/04 16:29:35 nonaka Exp $ */
/* $NetBSD: nvme.h,v 1.2 2017/04/29 00:06:40 nonaka Exp $ */
/*-
* Copyright (C) 2012-2013 Intel Corporation
@ -25,13 +25,14 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: head/sys/dev/nvme/nvme.h 296617 2016-03-10 17:13:10Z mav $
* $FreeBSD: head/sys/dev/nvme/nvme.h 314888 2017-03-07 23:02:59Z imp $
*/
#ifndef __NVME_H__
#define __NVME_H__
#define NVME_MAX_XFER_SIZE MAXPHYS
/* Cap nvme to 1MB transfers driver explodes with larger sizes */
#define NVME_MAX_XFER_SIZE (MAXPHYS < (1<<20) ? MAXPHYS : (1<<20))
/* Get/Set Features */
#define NVME_FEAT_ARBITRATION 0x01
@ -60,6 +61,19 @@
#define NVME_LOG_CHANGED_NAMESPACE_LIST 0x04
#define NVME_LOG_COMMAND_EFFECTS_LOG 0x05
#define NVME_LOG_RESERVATION_NOTIFICATION 0x80
/*
* The following are Intel Specific log pages, but they seem
* to be widely implemented.
*/
#define INTEL_LOG_READ_LAT_LOG 0xc1
#define INTEL_LOG_WRITE_LAT_LOG 0xc2
#define INTEL_LOG_TEMP_STATS 0xc5
#define INTEL_LOG_ADD_SMART 0xca
#define INTEL_LOG_DRIVE_MKT_NAME 0xdd
/*
* HGST log page, with lots ofs sub pages.
*/
#define HGST_INFO_LOG 0xc1
/* Error Information Log (Log Identifier 01h) */
struct nvme_error_information_entry {
@ -101,9 +115,9 @@ struct nvme_health_information_page {
uint64_t unsafe_shutdowns[2];
uint64_t media_errors[2];
uint64_t num_error_info_log_entries[2];
uint32_t warning_composite_temperature_time;
uint32_t critical_composite_temperature_time;
uint16_t temperature_sensor[8];
uint32_t warning_temp_time;
uint32_t error_temp_time;
uint16_t temp_sensor[8];
uint8_t reserved[296];
} __packed __aligned(4);
@ -126,6 +140,18 @@ struct nvme_firmware_page {
uint8_t reserved[448];
} __packed __aligned(4);
struct intel_log_temp_stats {
uint64_t current;
uint64_t overtemp_flag_last;
uint64_t overtemp_flag_life;
uint64_t max_temp;
uint64_t min_temp;
uint64_t _rsvd[5];
uint64_t max_oper_temp;
uint64_t min_oper_temp;
uint64_t est_offset;
} __packed __aligned(4);
/* Commands Supported and Effects (Log Identifier 05h) */
struct nvme_command_effeects_page {
uint32_t acs[256];

View File

@ -1,4 +1,4 @@
.\" $NetBSD: nvmectl.8,v 1.2 2016/06/05 09:13:08 wiz Exp $
.\" $NetBSD: nvmectl.8,v 1.3 2017/04/29 00:06:40 nonaka Exp $
.\"
.\" Copyright (c) 2012 Intel Corporation
.\" All rights reserved.
@ -32,7 +32,7 @@
.\"
.\" Author: Jim Harris <jimharris@FreeBSD.org>
.\"
.\" $FreeBSD: head/sbin/nvmecontrol/nvmecontrol.8 299151 2016-05-06 03:11:34Z pfg $
.\" $FreeBSD: head/sbin/nvmecontrol/nvmecontrol.8 314230 2017-02-25 00:09:16Z imp $
.\"
.Dd May 19, 2016
.Dt NVMECTL 8
@ -62,6 +62,8 @@
.Ic logpage
.Op Fl x
.Op Fl p Ar page_id
.Op Fl v Ar vendor-string
.Op Fl b
.Ar device_id Ns | Ns Ar namespace_id
.\".Nm
.\".Ic firmware
@ -75,9 +77,56 @@
.Op Fl p Ar power_state
.Op Fl w Ar workload_hint
.Ar device_id
.Nm
.Ic wdc cap-diag
.Op Fl o path_template
.Ar device id
.Nm
.Ic wdc drive-log
.Op Fl o path_template
.Ar device id
.Nm
.Ic wdc get-crash-dump
.Op Fl o path_template
.Ar device id
.\" .Nm
.\" .Ic wdc purge
.\" .Aq device id
.\" .Nm
.\" .Ic wdc purge-monitor
.\" .Aq device id
.Sh DESCRIPTION
NVM Express (NVMe) is a storage protocol standard, for SSDs and other
high-speed storage devices over PCI Express.
.Pp
.Ss logpage
The logpage command knows how to print log pages of various types.
It also knows about vendor specific log pages from hgst/wdc and intel.
Page 0xc1 for hgst/wdc contains the advanced smart information about
the drive.
Page 0xc1 is read latency stats for intel.
Page 0xc2 is write latency stats for intel.
Page 0xc5 is temperature stats for intel.
Page 0xca is advanced smart information for intel.
.Pp
Specifying
.Fl p
.Ic help
will list all valid vendors and pages.
.Fl x
will print the page as hex.
.Fl b
will print the binary data for the page.
.Ss wdc
The various wdc command retrieve log data from the wdc/hgst drives.
The
.Fl o
flag specifies a path template to use to output the files.
Each file takes the path template (which defaults to nothing), appends
the drive's serial number and the type of dump it is followed
by .bin.
These logs must be sent to the vendor for analysis.
This tool only provides a way to extract them.
.Sh EXAMPLES
.Dl nvmectl devlist
.Pp
@ -95,9 +144,9 @@ data for namespace 1.
.\".Pp
.\".Dl nvmectl perftest -n 32 -o read -s 512 -t 30 nvme0ns1
.\".Pp
.\"Run a performance test on nvme0ns1 using 32 kernel threads for 30 seconds. Each
.\"thread will issue a single 512 byte read command. Results are printed to
.\"stdout when 30 seconds expires.
.\"Run a performance test on nvme0ns1 using 32 kernel threads for 30 seconds.
.\"Each thread will issue a single 512 byte read command.
.\"Results are printed to stdout when 30 seconds expires.
.\".Pp
.\".Dl nvmectl reset nvme0
.\".Pp
@ -109,9 +158,20 @@ Display a human-readable summary of the nvme0 controller's Error Information Log
Log pages defined by the NVMe specification include Error Information Log (ID=1),
SMART/Health Information Log (ID=2), and Firmware Slot Log (ID=3).
.Pp
.Dl nvmectl logpage -p 0xc1 -v wdc nvme0
.Pp
Display a human-readable summary of the nvme0's wdc-specific advanced
SMART data.
.Pp
.Dl nvmectl logpage -p 1 -x nvme0
.Pp
Display a hexadecimal dump of the nvme0 controller's Error Information Log.
.Pp
.Dl nvmectl logpage -p 0xcb -b nvme0 > /tmp/page-cb.bin
.Pp
Print the contents of vendor specific page 0xcb as binary data on
standard out.
Redirect it to a temporary file.
.\".Pp
.\".Dl nvmectl firmware -s 2 -f /tmp/nvme_firmware nvme0
.\".Pp
@ -138,6 +198,9 @@ Set the current power mode.
.Dl nvmectl power nvme0
.Pp
Get the current power mode.
.Sh HISTORY
The nvmecontrol utility appeared in
.Fx 9.2 .
.Sh AUTHORS
.An -nosplit
nvmecontrol was developed by Intel and originally written by

View File

@ -1,4 +1,4 @@
/* $NetBSD: nvmectl.c,v 1.2 2016/06/04 20:59:49 joerg Exp $ */
/* $NetBSD: nvmectl.c,v 1.3 2017/04/29 00:06:40 nonaka Exp $ */
/*-
* Copyright (C) 2012-2013 Intel Corporation
@ -28,9 +28,9 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: nvmectl.c,v 1.2 2016/06/04 20:59:49 joerg Exp $");
__RCSID("$NetBSD: nvmectl.c,v 1.3 2017/04/29 00:06:40 nonaka Exp $");
#if 0
__FBSDID("$FreeBSD: head/sbin/nvmecontrol/nvmecontrol.c 295087 2016-01-30 22:48:06Z imp $");
__FBSDID("$FreeBSD: head/sbin/nvmecontrol/nvmecontrol.c 314229 2017-02-25 00:09:12Z imp $");
#endif
#endif
@ -52,13 +52,7 @@ __FBSDID("$FreeBSD: head/sbin/nvmecontrol/nvmecontrol.c 295087 2016-01-30 22:48:
#include "nvmectl.h"
typedef void (*nvme_fn_t)(int argc, char *argv[]);
static struct nvme_function {
const char *name;
nvme_fn_t fn;
const char *usage;
} funcs[] = {
static struct nvme_function funcs[] = {
{"devlist", devlist, DEVLIST_USAGE},
{"identify", identify, IDENTIFY_USAGE},
#ifdef PERFTEST_USAGE
@ -72,15 +66,14 @@ static struct nvme_function {
{"firmware", firmware, FIRMWARE_USAGE},
#endif
{"power", power, POWER_USAGE},
{"wdc", wdc, WDC_USAGE},
{NULL, NULL, NULL},
};
__dead static void
usage(void)
void
gen_usage(struct nvme_function *f)
{
struct nvme_function *f;
f = funcs;
fprintf(stderr, "usage:\n");
while (f->name != NULL) {
fprintf(stderr, "%s", f->usage);
@ -89,6 +82,26 @@ usage(void)
exit(1);
}
void
dispatch(int argc, char *argv[], struct nvme_function *tbl)
{
struct nvme_function *f = tbl;
if (argv[1] == NULL) {
gen_usage(tbl);
return;
}
while (f->name != NULL) {
if (strcmp(argv[1], f->name) == 0)
f->fn(argc-1, &argv[1]);
f++;
}
fprintf(stderr, "Unknown command: %s\n", argv[1]);
gen_usage(tbl);
}
static void
print_bytes(void *data, uint32_t length)
{
@ -269,19 +282,11 @@ nvme_strvis(u_char *dst, int dlen, const u_char *src, int slen)
int
main(int argc, char *argv[])
{
struct nvme_function *f;
if (argc < 2)
usage();
gen_usage(funcs);
f = funcs;
while (f->name != NULL) {
if (strcmp(argv[1], f->name) == 0)
f->fn(argc-1, &argv[1]);
f++;
}
usage();
dispatch(argc, argv, funcs);
return (0);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: nvmectl.h,v 1.2 2016/06/04 20:59:49 joerg Exp $ */
/* $NetBSD: nvmectl.h,v 1.3 2017/04/29 00:06:40 nonaka Exp $ */
/*-
* Copyright (C) 2012-2013 Intel Corporation
@ -25,7 +25,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: head/sbin/nvmecontrol/nvmecontrol.h 295087 2016-01-30 22:48:06Z imp $
* $FreeBSD: head/sbin/nvmecontrol/nvmecontrol.h 314230 2017-02-25 00:09:16Z imp $
*/
#ifndef __NVMECTL_H__
@ -36,6 +36,14 @@
#include <dev/ic/nvmeio.h>
#include "nvme.h"
typedef void (*nvme_fn_t)(int argc, char *argv[]);
struct nvme_function {
const char *name;
nvme_fn_t fn;
const char *usage;
};
#define NVME_CTRLR_PREFIX "nvme"
#define NVME_NS_PREFIX "ns"
@ -47,9 +55,9 @@
#if 0
#define PERFTEST_USAGE \
" nvmectl perftest <-n num_threads> <-o read|write>\n" \
" nvmectl perftest <-n num_threads> <-o read|write>\n" \
" <-s size_in_bytes> <-t time_in_seconds>\n" \
" <-i intr|wait> [-f refthread] [-p]\n" \
" <-i intr|wait> [-f refthread] [-p]\n" \
" <namespace id>\n"
#endif
@ -59,7 +67,8 @@
#endif
#define LOGPAGE_USAGE \
" nvmectl logpage <-p page_id> [-x] <controller id|namespace id>\n" \
" nvmectl logpage <-p page_id> [-b] [-v vendor] [-x] " \
"<controller id|namespace id>\n"
#if 0
#define FIRMWARE_USAGE \
@ -69,19 +78,23 @@
#define POWER_USAGE \
" nvmectl power [-l] [-p new-state [-w workload-hint]] <controller id>\n"
#define WDC_USAGE \
" nvmecontrol wdc (cap-diag|drive-log|get-crash-dump|purge|purge-montior)\n"
void devlist(int, char *[]) __dead;
void identify(int, char *[]) __dead;
#ifdef PERFTEST_USAGE
void perftest(int, char *[]);
void perftest(int, char *[]) __dead;
#endif
#ifdef RESET_USAGE
void reset(int, char *[]);
void reset(int, char *[]) __dead;
#endif
void logpage(int, char *[]) __dead;
#ifdef FIRMWARE_USAGE
void firmware(int, char *[]);
void firmware(int, char *[]) __dead;
#endif
void power(int, char *[]) __dead;
void wdc(int, char *[]) __dead;
int open_dev(const char *, int *, int, int);
void parse_ns_str(const char *, char *, int *);
@ -89,6 +102,8 @@ void read_controller_data(int, struct nvm_identify_controller *);
void read_namespace_data(int, int, struct nvm_identify_namespace *);
void print_hex(void *, uint32_t);
void read_logpage(int, uint8_t, int, void *, uint32_t);
void gen_usage(struct nvme_function *) __dead;
void dispatch(int argc, char *argv[], struct nvme_function *f);
void nvme_strvis(uint8_t *, int, const uint8_t *, int);
#endif /* __NVMECTL_H__ */