Decode few more NVMe log pages.

In particular: Changed Namespace List, Commands Supported and Effects,
Reservation Notification, Sanitize Status.

Add few new arguments to `nvmecontrol log` subcommand.

MFC after:	2 weeks
Sponsored by:	iXsystems, Inc.
This commit is contained in:
mav 2019-08-02 20:16:21 +00:00
parent 6d341cc8f0
commit 2d53375958
6 changed files with 414 additions and 14 deletions

View File

@ -104,7 +104,7 @@ slot_has_valid_firmware(int fd, int slot)
int has_fw = false;
read_logpage(fd, NVME_LOG_FIRMWARE_SLOT,
NVME_GLOBAL_NAMESPACE_TAG, &fw, sizeof(fw));
NVME_GLOBAL_NAMESPACE_TAG, 0, 0, 0, &fw, sizeof(fw));
if (fw.revision[slot-1] != 0LLU)
has_fw = true;

View File

@ -6,6 +6,7 @@
*
* Copyright (C) 2012-2013 Intel Corporation
* All rights reserved.
* Copyright (C) 2018-2019 Alexander Motin <mav@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -57,12 +58,18 @@ static struct options {
bool binary;
bool hex;
uint32_t page;
uint8_t lsp;
uint16_t lsi;
bool rae;
const char *vendor;
const char *dev;
} opt = {
.binary = false,
.hex = false,
.page = NONE,
.lsp = 0,
.lsi = 0,
.rae = false,
.vendor = NULL,
.dev = NULL,
};
@ -75,6 +82,12 @@ static const struct opts logpage_opts[] = {
"Dump the log page as hex"),
OPT("page", 'p', arg_uint32, opt, page,
"Page to dump"),
OPT("lsp", 'f', arg_uint8, opt, lsp,
"Log Specific Field"),
OPT("lsi", 'i', arg_uint16, opt, lsp,
"Log Specific Identifier"),
OPT("rae", 'r', arg_none, opt, rae,
"Retain Asynchronous Event"),
OPT("vendor", 'v', arg_string, opt, vendor,
"Vendor specific formatting"),
{ NULL, 0, arg_none, NULL, NULL }
@ -103,11 +116,38 @@ CMD_COMMAND(logpage_cmd);
static SLIST_HEAD(,logpage_function) logpages;
static int
logpage_compare(struct logpage_function *a, struct logpage_function *b)
{
int c;
if ((a->vendor == NULL) != (b->vendor == NULL))
return (a->vendor == NULL ? -1 : 1);
if (a->vendor != NULL) {
c = strcmp(a->vendor, b->vendor);
if (c != 0)
return (c);
}
return ((int)a->log_page - (int)b->log_page);
}
void
logpage_register(struct logpage_function *p)
{
struct logpage_function *l, *a;
SLIST_INSERT_HEAD(&logpages, p, link);
a = NULL;
l = SLIST_FIRST(&logpages);
while (l != NULL) {
if (logpage_compare(l, p) > 0)
break;
a = l;
l = SLIST_NEXT(l, link);
}
if (a == NULL)
SLIST_INSERT_HEAD(&logpages, p, link);
else
SLIST_INSERT_AFTER(a, p, link);
}
const char *
@ -150,19 +190,28 @@ get_log_buffer(uint32_t size)
}
void
read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload,
uint32_t payload_size)
read_logpage(int fd, uint8_t log_page, uint32_t nsid, uint8_t lsp,
uint16_t lsi, uint8_t rae, void *payload, uint32_t payload_size)
{
struct nvme_pt_command pt;
struct nvme_error_information_entry *err_entry;
int i, err_pages;
u_int i, err_pages, numd;
numd = payload_size / sizeof(uint32_t) - 1;
memset(&pt, 0, sizeof(pt));
pt.cmd.opc = NVME_OPC_GET_LOG_PAGE;
pt.cmd.nsid = htole32(nsid);
pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16;
pt.cmd.cdw10 |= log_page;
pt.cmd.cdw10 = htole32(pt.cmd.cdw10);
pt.cmd.cdw10 = htole32(
(numd << 16) | /* NUMDL */
(rae << 15) | /* RAE */
(lsp << 8) | /* LSP */
log_page); /* LID */
pt.cmd.cdw11 = htole32(
((uint32_t)lsi << 16) | /* LSI */
(numd >> 16)); /* NUMDU */
pt.cmd.cdw12 = 0; /* LPOL */
pt.cmd.cdw13 = 0; /* LPOU */
pt.cmd.cdw14 = 0; /* UUID Index */
pt.buf = payload;
pt.len = payload_size;
pt.is_read = 1;
@ -186,6 +235,21 @@ read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload,
nvme_firmware_page_swapbytes(
(struct nvme_firmware_page *)payload);
break;
case NVME_LOG_CHANGED_NAMESPACE:
nvme_ns_list_swapbytes((struct nvme_ns_list *)payload);
break;
case NVME_LOG_COMMAND_EFFECT:
nvme_command_effects_page_swapbytes(
(struct nvme_command_effects_page *)payload);
break;
case NVME_LOG_RES_NOTIFICATION:
nvme_res_notification_page_swapbytes(
(struct nvme_res_notification_page *)payload);
break;
case NVME_LOG_SANITIZE_STATUS:
nvme_sanitize_status_page_swapbytes(
(struct nvme_sanitize_status_page *)payload);
break;
case INTEL_LOG_TEMP_STATS:
intel_log_temp_stats_swapbytes(
(struct intel_log_temp_stats *)payload);
@ -369,6 +433,160 @@ print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t
}
}
static void
print_log_ns(const struct nvme_controller_data *cdata __unused, void *buf,
uint32_t size __unused)
{
struct nvme_ns_list *nsl;
u_int i;
nsl = (struct nvme_ns_list *)buf;
printf("Changed Namespace List\n");
printf("======================\n");
for (i = 0; i < nitems(nsl->ns) && nsl->ns[i] != 0; i++) {
printf("%08x\n", nsl->ns[i]);
}
}
static void
print_log_command_effects(const struct nvme_controller_data *cdata __unused,
void *buf, uint32_t size __unused)
{
struct nvme_command_effects_page *ce;
u_int i;
uint32_t s;
ce = (struct nvme_command_effects_page *)buf;
printf("Commands Supported and Effects\n");
printf("==============================\n");
printf(" Command\tLBCC\tNCC\tNIC\tCCC\tCSE\tUUID\n");
for (i = 0; i < 255; i++) {
s = ce->acs[i];
if (((s >> NVME_CE_PAGE_CSUP_SHIFT) &
NVME_CE_PAGE_CSUP_MASK) == 0)
continue;
printf("Admin\t%02x\t%s\t%s\t%s\t%s\t%u\t%s\n", i,
((s >> NVME_CE_PAGE_LBCC_SHIFT) &
NVME_CE_PAGE_LBCC_MASK) ? "Yes" : "No",
((s >> NVME_CE_PAGE_NCC_SHIFT) &
NVME_CE_PAGE_NCC_MASK) ? "Yes" : "No",
((s >> NVME_CE_PAGE_NIC_SHIFT) &
NVME_CE_PAGE_NIC_MASK) ? "Yes" : "No",
((s >> NVME_CE_PAGE_CCC_SHIFT) &
NVME_CE_PAGE_CCC_MASK) ? "Yes" : "No",
((s >> NVME_CE_PAGE_CSE_SHIFT) &
NVME_CE_PAGE_CSE_MASK),
((s >> NVME_CE_PAGE_UUID_SHIFT) &
NVME_CE_PAGE_UUID_MASK) ? "Yes" : "No");
}
for (i = 0; i < 255; i++) {
s = ce->iocs[i];
if (((s >> NVME_CE_PAGE_CSUP_SHIFT) &
NVME_CE_PAGE_CSUP_MASK) == 0)
continue;
printf("I/O\t%02x\t%s\t%s\t%s\t%s\t%u\t%s\n", i,
((s >> NVME_CE_PAGE_LBCC_SHIFT) &
NVME_CE_PAGE_LBCC_MASK) ? "Yes" : "No",
((s >> NVME_CE_PAGE_NCC_SHIFT) &
NVME_CE_PAGE_NCC_MASK) ? "Yes" : "No",
((s >> NVME_CE_PAGE_NIC_SHIFT) &
NVME_CE_PAGE_NIC_MASK) ? "Yes" : "No",
((s >> NVME_CE_PAGE_CCC_SHIFT) &
NVME_CE_PAGE_CCC_MASK) ? "Yes" : "No",
((s >> NVME_CE_PAGE_CSE_SHIFT) &
NVME_CE_PAGE_CSE_MASK),
((s >> NVME_CE_PAGE_UUID_SHIFT) &
NVME_CE_PAGE_UUID_MASK) ? "Yes" : "No");
}
}
static void
print_log_res_notification(const struct nvme_controller_data *cdata __unused,
void *buf, uint32_t size __unused)
{
struct nvme_res_notification_page *rn;
rn = (struct nvme_res_notification_page *)buf;
printf("Reservation Notification\n");
printf("========================\n");
printf("Log Page Count: %ju\n", rn->log_page_count);
printf("Log Page Type: ");
switch (rn->log_page_type) {
case 0:
printf("Empty Log Page\n");
break;
case 1:
printf("Registration Preempted\n");
break;
case 2:
printf("Reservation Released\n");
break;
case 3:
printf("Reservation Preempted\n");
break;
default:
printf("Unknown %x\n", rn->log_page_type);
break;
};
printf("Number of Available Log Pages: %d\n", rn->available_log_pages);
printf("Namespace ID: 0x%x\n", rn->nsid);
}
static void
print_log_sanitize_status(const struct nvme_controller_data *cdata __unused,
void *buf, uint32_t size __unused)
{
struct nvme_sanitize_status_page *ss;
u_int p;
ss = (struct nvme_sanitize_status_page *)buf;
printf("Sanitize Status\n");
printf("===============\n");
printf("Sanitize Progress: %u%% (%u/65535)\n",
(ss->sprog * 100 + 32768) / 65536, ss->sprog);
printf("Sanitize Status: ");
switch ((ss->sstat >> NVME_SS_PAGE_SSTAT_STATUS_SHIFT) &
NVME_SS_PAGE_SSTAT_STATUS_MASK) {
case NVME_SS_PAGE_SSTAT_STATUS_NEVER:
printf("Never sanitized");
break;
case NVME_SS_PAGE_SSTAT_STATUS_COMPLETED:
printf("Completed");
break;
case NVME_SS_PAGE_SSTAT_STATUS_INPROG:
printf("In Progress");
break;
case NVME_SS_PAGE_SSTAT_STATUS_FAILED:
printf("Failed");
break;
case NVME_SS_PAGE_SSTAT_STATUS_COMPLETEDWD:
printf("Completed with deallocation");
break;
default:
printf("Unknown");
break;
}
p = (ss->sstat & NVME_SS_PAGE_SSTAT_PASSES_SHIFT) >>
NVME_SS_PAGE_SSTAT_PASSES_MASK;
if (p > 0)
printf(", %d passes", p);
if ((ss->sstat & NVME_SS_PAGE_SSTAT_GDE_SHIFT) >>
NVME_SS_PAGE_SSTAT_GDE_MASK)
printf(", Global Data Erased");
printf("\n");
printf("Sanitize Command Dword 10: 0x%x\n", ss->scdw10);
printf("Time For Overwrite: %u sec\n", ss->etfo);
printf("Time For Block Erase: %u sec\n", ss->etfbe);
printf("Time For Crypto Erase: %u sec\n", ss->etfce);
printf("Time For Overwrite No-Deallocate: %u sec\n", ss->etfownd);
printf("Time For Block Erase No-Deallocate: %u sec\n", ss->etfbewnd);
printf("Time For Crypto Erase No-Deallocate: %u sec\n", ss->etfcewnd);
}
/*
* Table of log page printer / sizing.
*
@ -384,6 +602,48 @@ NVME_LOGPAGE(health,
NVME_LOGPAGE(fw,
NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information",
print_log_firmware, sizeof(struct nvme_firmware_page));
NVME_LOGPAGE(ns,
NVME_LOG_CHANGED_NAMESPACE, NULL, "Changed Namespace List",
print_log_ns, sizeof(struct nvme_ns_list));
NVME_LOGPAGE(ce,
NVME_LOG_COMMAND_EFFECT, NULL, "Commands Supported and Effects",
print_log_command_effects, sizeof(struct nvme_command_effects_page));
NVME_LOGPAGE(dst,
NVME_LOG_DEVICE_SELF_TEST, NULL, "Device Self-test",
NULL, 564);
NVME_LOGPAGE(thi,
NVME_LOG_TELEMETRY_HOST_INITIATED, NULL, "Telemetry Host-Initiated",
NULL, DEFAULT_SIZE);
NVME_LOGPAGE(tci,
NVME_LOG_TELEMETRY_CONTROLLER_INITIATED, NULL, "Telemetry Controller-Initiated",
NULL, DEFAULT_SIZE);
NVME_LOGPAGE(egi,
NVME_LOG_ENDURANCE_GROUP_INFORMATION, NULL, "Endurance Group Information",
NULL, DEFAULT_SIZE);
NVME_LOGPAGE(plpns,
NVME_LOG_PREDICTABLE_LATENCY_PER_NVM_SET, NULL, "Predictable Latency Per NVM Set",
NULL, DEFAULT_SIZE);
NVME_LOGPAGE(ple,
NVME_LOG_PREDICTABLE_LATENCY_EVENT_AGGREGATE, NULL, "Predictable Latency Event Aggregate",
NULL, DEFAULT_SIZE);
NVME_LOGPAGE(ana,
NVME_LOG_ASYMMETRIC_NAMESPAVE_ACCESS, NULL, "Asymmetric Namespace Access",
NULL, DEFAULT_SIZE);
NVME_LOGPAGE(pel,
NVME_LOG_PERSISTENT_EVENT_LOG, NULL, "Persistent Event Log",
NULL, DEFAULT_SIZE);
NVME_LOGPAGE(lbasi,
NVME_LOG_LBA_STATUS_INFORMATION, NULL, "LBA Status Information",
NULL, DEFAULT_SIZE);
NVME_LOGPAGE(egea,
NVME_LOG_ENDURANCE_GROUP_EVENT_AGGREGATE, NULL, "Endurance Group Event Aggregate",
NULL, DEFAULT_SIZE);
NVME_LOGPAGE(res_notification,
NVME_LOG_RES_NOTIFICATION, NULL, "Reservation Notification",
print_log_res_notification, sizeof(struct nvme_res_notification_page));
NVME_LOGPAGE(sanitize_status,
NVME_LOG_SANITIZE_STATUS, NULL, "Sanitize Status",
print_log_sanitize_status, sizeof(struct nvme_sanitize_status_page));
static void
logpage_help(void)
@ -475,7 +735,8 @@ logpage(const struct cmd *f, int argc, char *argv[])
continue;
if (opt.page != lpf->log_page)
continue;
print_fn = lpf->print_fn;
if (lpf->print_fn != NULL)
print_fn = lpf->print_fn;
size = lpf->size;
break;
}
@ -488,7 +749,7 @@ logpage(const struct cmd *f, int argc, char *argv[])
/* Read the log page */
buf = get_log_buffer(size);
read_logpage(fd, opt.page, nsid, buf, size);
read_logpage(fd, opt.page, nsid, opt.lsp, opt.lsi, opt.rae, buf, size);
print_fn(&cdata, buf, size);
close(fd);

View File

@ -34,7 +34,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd August 1, 2019
.Dd August 2, 2019
.Dt NVMECONTROL 8
.Os
.Sh NAME
@ -66,6 +66,9 @@
.Op Fl x
.Op Fl v Ar vendor-string
.Op Fl b
.Op Fl f Ar LSP
.Op Fl i Ar LSI
.Op Fl r
.Aq device id
.Aq namespace id
.Nm
@ -172,6 +175,14 @@ Drive Error Log
Health/SMART Data
.It Dv Page 0x03
Firmware Information
.It Dv Page 0x04
Changed Namespace List
.It Dv Page 0x05
Commands Supported and Effects
.It Dv Page 0x80
Reservation Notification
.It Dv Page 0x81
Sanitize Status
.It Dv Page 0xc1
Advanced SMART information (WDC/HGST)
.It Dv Page 0xc1
@ -185,13 +196,19 @@ Advanced SMART information (Intel)
.El
.Pp
Specifying
.Fl p
.Fl v
.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.
.Fl s
will set Log Specific Field.
.Fl i
will set Log Specific Identifier.
.Fl r
will set Retain Asynchronous Event.
.Ss ns
Various namespace management commands.
If namespace management is supported by device, allow list, create and delete

View File

@ -74,8 +74,8 @@ void read_controller_data(int fd, struct nvme_controller_data *cdata);
void read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata);
void print_hex(void *data, uint32_t length);
void print_namespace(struct nvme_namespace_data *nsdata);
void read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload,
uint32_t payload_size);
void read_logpage(int fd, uint8_t log_page, uint32_t nsid, uint8_t lsp,
uint16_t lsi, uint8_t rae, void *payload, uint32_t payload_size);
void print_temp(uint16_t t);
void print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused);

View File

@ -445,6 +445,35 @@ enum nvme_critical_warning_state {
#define NVME_FIRMWARE_PAGE_AFI_SLOT_SHIFT (0)
#define NVME_FIRMWARE_PAGE_AFI_SLOT_MASK (0x7)
/* Commands Supported and Effects */
#define NVME_CE_PAGE_CSUP_SHIFT (0)
#define NVME_CE_PAGE_CSUP_MASK (0x1)
#define NVME_CE_PAGE_LBCC_SHIFT (1)
#define NVME_CE_PAGE_LBCC_MASK (0x1)
#define NVME_CE_PAGE_NCC_SHIFT (2)
#define NVME_CE_PAGE_NCC_MASK (0x1)
#define NVME_CE_PAGE_NIC_SHIFT (3)
#define NVME_CE_PAGE_NIC_MASK (0x1)
#define NVME_CE_PAGE_CCC_SHIFT (4)
#define NVME_CE_PAGE_CCC_MASK (0x1)
#define NVME_CE_PAGE_CSE_SHIFT (16)
#define NVME_CE_PAGE_CSE_MASK (0x7)
#define NVME_CE_PAGE_UUID_SHIFT (19)
#define NVME_CE_PAGE_UUID_MASK (0x1)
/* Sanitize Status */
#define NVME_SS_PAGE_SSTAT_STATUS_SHIFT (0)
#define NVME_SS_PAGE_SSTAT_STATUS_MASK (0x7)
#define NVME_SS_PAGE_SSTAT_STATUS_NEVER (0)
#define NVME_SS_PAGE_SSTAT_STATUS_COMPLETED (1)
#define NVME_SS_PAGE_SSTAT_STATUS_INPROG (2)
#define NVME_SS_PAGE_SSTAT_STATUS_FAILED (3)
#define NVME_SS_PAGE_SSTAT_STATUS_COMPLETEDWD (4)
#define NVME_SS_PAGE_SSTAT_PASSES_SHIFT (3)
#define NVME_SS_PAGE_SSTAT_PASSES_MASK (0x1f)
#define NVME_SS_PAGE_SSTAT_GDE_SHIFT (8)
#define NVME_SS_PAGE_SSTAT_GDE_MASK (0x1)
/* CC register SHN field values */
enum shn_value {
NVME_SHN_NORMAL = 0x1,
@ -1291,6 +1320,43 @@ struct nvme_ns_list {
_Static_assert(sizeof(struct nvme_ns_list) == 4096, "bad size for nvme_ns_list");
struct nvme_command_effects_page {
uint32_t acs[256];
uint32_t iocs[256];
uint8_t reserved[2048];
} __packed __aligned(4);
_Static_assert(sizeof(struct nvme_command_effects_page) == 4096,
"bad size for nvme_command_effects_page");
struct nvme_res_notification_page {
uint64_t log_page_count;
uint8_t log_page_type;
uint8_t available_log_pages;
uint8_t reserved2;
uint32_t nsid;
uint8_t reserved[48];
} __packed __aligned(4);
_Static_assert(sizeof(struct nvme_res_notification_page) == 64,
"bad size for nvme_res_notification_page");
struct nvme_sanitize_status_page {
uint16_t sprog;
uint16_t sstat;
uint32_t scdw10;
uint32_t etfo;
uint32_t etfbe;
uint32_t etfce;
uint32_t etfownd;
uint32_t etfbewnd;
uint32_t etfcewnd;
uint8_t reserved[480];
} __packed __aligned(4);
_Static_assert(sizeof(struct nvme_sanitize_status_page) == 512,
"bad size for nvme_sanitize_status_page");
struct intel_log_temp_stats
{
uint64_t current;
@ -1719,6 +1785,38 @@ void nvme_ns_list_swapbytes(struct nvme_ns_list *s)
s->ns[i] = le32toh(s->ns[i]);
}
static inline
void nvme_command_effects_page_swapbytes(struct nvme_command_effects_page *s)
{
int i;
for (i = 0; i < 256; i++)
s->acs[i] = le32toh(s->acs[i]);
for (i = 0; i < 256; i++)
s->iocs[i] = le32toh(s->iocs[i]);
}
static inline
void nvme_res_notification_page_swapbytes(struct nvme_res_notification_page *s)
{
s->log_page_count = le64toh(s->log_page_count);
s->nsid = le32toh(s->nsid);
}
static inline
void nvme_sanitize_status_page_swapbytes(struct nvme_sanitize_status_page *s)
{
s->sprog = le16toh(s->sprog);
s->sstat = le16toh(s->sstat);
s->scdw10 = le32toh(s->scdw10);
s->etfo = le32toh(s->etfo);
s->etfbe = le32toh(s->etfbe);
s->etfce = le32toh(s->etfce);
s->etfownd = le32toh(s->etfownd);
s->etfbewnd = le32toh(s->etfbewnd);
s->etfcewnd = le32toh(s->etfcewnd);
}
static inline
void intel_log_temp_stats_swapbytes(struct intel_log_temp_stats *s)
{

View File

@ -570,6 +570,9 @@ is_log_page_id_valid(uint8_t page_id)
case NVME_LOG_HEALTH_INFORMATION:
case NVME_LOG_FIRMWARE_SLOT:
case NVME_LOG_CHANGED_NAMESPACE:
case NVME_LOG_COMMAND_EFFECT:
case NVME_LOG_RES_NOTIFICATION:
case NVME_LOG_SANITIZE_STATUS:
return (TRUE);
}
@ -596,6 +599,15 @@ nvme_ctrlr_get_log_page_size(struct nvme_controller *ctrlr, uint8_t page_id)
case NVME_LOG_CHANGED_NAMESPACE:
log_page_size = sizeof(struct nvme_ns_list);
break;
case NVME_LOG_COMMAND_EFFECT:
log_page_size = sizeof(struct nvme_command_effects_page);
break;
case NVME_LOG_RES_NOTIFICATION:
log_page_size = sizeof(struct nvme_res_notification_page);
break;
case NVME_LOG_SANITIZE_STATUS:
log_page_size = sizeof(struct nvme_sanitize_status_page);
break;
default:
log_page_size = 0;
break;
@ -666,6 +678,18 @@ nvme_ctrlr_async_event_log_page_cb(void *arg, const struct nvme_completion *cpl)
nvme_ns_list_swapbytes(
(struct nvme_ns_list *)aer->log_page_buffer);
break;
case NVME_LOG_COMMAND_EFFECT:
nvme_command_effects_page_swapbytes(
(struct nvme_command_effects_page *)aer->log_page_buffer);
break;
case NVME_LOG_RES_NOTIFICATION:
nvme_res_notification_page_swapbytes(
(struct nvme_res_notification_page *)aer->log_page_buffer);
break;
case NVME_LOG_SANITIZE_STATUS:
nvme_sanitize_status_page_swapbytes(
(struct nvme_sanitize_status_page *)aer->log_page_buffer);
break;
case INTEL_LOG_TEMP_STATS:
intel_log_temp_stats_swapbytes(
(struct intel_log_temp_stats *)aer->log_page_buffer);