Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144256632
D32700.id101343.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
29 KB
Referenced Files
None
Subscribers
None
D32700.id101343.diff
View Options
Index: sbin/nvmecontrol/Makefile
===================================================================
--- sbin/nvmecontrol/Makefile
+++ sbin/nvmecontrol/Makefile
@@ -10,6 +10,7 @@
SRCS+= passthru.c
SRCS+= identify_ext.c nvme_util.c nc_util.c
SRCS+= selftest.c
+SRCS+= feature.c
MAN= nvmecontrol.8
LDFLAGS+= -rdynamic
LIBADD+= util
Index: sbin/nvmecontrol/feature.c
===================================================================
--- /dev/null
+++ sbin/nvmecontrol/feature.c
@@ -0,0 +1,837 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (C) 2022 Wanpeng Qian <wanpengqian@gmail.org>
+ *
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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 ``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 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 <sys/stat.h>
+
+#include <stdint.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "nvmecontrol.h"
+
+/* Tables for command line parsing */
+
+static cmd_fn_t feature;
+static cmd_fn_t feature_get;
+static cmd_fn_t feature_set;
+static cmd_fn_t feature_summary;
+
+#define NONE 0xffu
+#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
+#define OPT_END { NULL, 0, arg_none, NULL, NULL }
+
+/* Convert a zero-based value into a one-based value */
+#define ONE_BASED(zero) ((zero) + 1)
+/* Convert a one-based value into a zero-based value */
+#define ZERO_BASED(one) ((one) - 1)
+
+static const char *
+lba_range_type_name_res[] = {
+ [0] = "General Purpose",
+ [1] = "Filesystem",
+ [2] = "RAID",
+ [3] = "Cache",
+ [4] = "Page / swap file",
+};
+static uint32_t lba_range_type_name_res_max =
+ nitems(lba_range_type_name_res);
+
+struct summary_data {
+ uint8_t fid;
+ uint8_t sel;
+ uint32_t cdw11;
+};
+
+struct nvme_lba_range_type {
+ uint8_t type;
+ uint8_t attribute;
+ uint8_t reserved1[14];
+ uint64_t slba;
+ uint64_t nlb;
+ uint8_t guid[16];
+ uint8_t reserved2[16];
+} __packed __aligned(4);
+
+static const struct summary_data feature_summary_list[] = {
+ { NVME_FEAT_ARBITRATION, 0x0, 0x0 },
+ { NVME_FEAT_POWER_MANAGEMENT, 0x0, 0x0 },
+ { NVME_FEAT_TEMPERATURE_THRESHOLD, 0x0, 0x0 },
+ { NVME_FEAT_ERROR_RECOVERY, 0x0, 0x0 },
+ { NVME_FEAT_NUMBER_OF_QUEUES, 0x0, 0x0 },
+ { NVME_FEAT_INTERRUPT_COALESCING, 0x0, 0x0 },
+ { NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION, 0x0, 0x0 },
+ { NVME_FEAT_WRITE_ATOMICITY, 0x0, 0x0 },
+ { NVME_FEAT_ASYNC_EVENT_CONFIGURATION, 0x0, 0x0 },
+};
+static uint32_t feature_summary_list_max = nitems(feature_summary_list);
+
+static struct cmd feature_cmd = {
+ .name = "feature",
+ .fn = feature,
+ .descr = "Get Feature & Set Feature for specified device",
+ .ctx_size = 0,
+ .opts = NULL,
+ .args = NULL,
+};
+
+CMD_COMMAND(feature_cmd);
+
+static struct get_options {
+ bool hex;
+ uint8_t fid;
+ uint8_t sel;
+ uint32_t cdw11;
+ const char *vendor;
+ const char *dev;
+} get_opt = {
+ .hex = false,
+ .fid = NONE,
+ .sel = 0,
+ .cdw11 = 0,
+ .vendor = NULL,
+ .dev = NULL,
+};
+
+static const struct opts get_opts[] = {
+ OPT("hex", 'x', arg_none, get_opt, hex,
+ "print result hex"),
+ OPT("fid", 'f', arg_uint8, get_opt, fid,
+ "feature id (required)"),
+ OPT("vendor", 'v', arg_string, get_opt, vendor,
+ "Vendor specific formatting"),
+ OPT("sel", 's', arg_uint8, get_opt, sel,
+ "[0-3]current/default/saved/supported"),
+ OPT("cdw11", '1', arg_uint32, get_opt, cdw11,
+ "dword 11 value"),
+ OPT_END
+};
+
+static const struct args get_args[] = {
+ { arg_string, &get_opt.dev, "controller-id" },
+ { arg_none, NULL, NULL },
+};
+
+static struct cmd get_cmd = {
+ .name = "get",
+ .fn = feature_get,
+ .descr = "Get Feature for specified device",
+ .ctx_size = sizeof(get_opt),
+ .opts = get_opts,
+ .args = get_args,
+};
+
+CMD_SUBCOMMAND(feature_cmd, get_cmd);
+
+static struct set_options {
+ uint8_t fid;
+ bool save;
+ uint32_t cdw11;
+ uint32_t cdw12;
+ uint32_t cdw13;
+ uint32_t cdw14;
+ uint32_t cdw15;
+ const char *vendor;
+ const char *datafile;
+ const char *dev;
+} set_opt = {
+ .fid = NONE,
+ .save = false,
+ .cdw11 = 0,
+ .cdw12 = 0,
+ .cdw13 = 0,
+ .cdw14 = 0,
+ .cdw15 = 0,
+ .vendor = NULL,
+ .datafile = NULL,
+ .dev = NULL,
+};
+
+static const struct opts set_opts[] = {
+ OPT("fid", 'f', arg_uint8, set_opt, fid,
+ "feature id (required)"),
+ OPT("vendor", 'v', arg_string, set_opt, vendor,
+ "Vendor specific formatting"),
+ OPT("save", 's', arg_none, set_opt, save,
+ "the controller shall save the attribute"),
+ OPT("cdw11", '1', arg_uint32, set_opt, cdw11,
+ "dword 11 value"),
+ OPT("cdw12", '2', arg_uint32, set_opt, cdw12,
+ "dword 12 value"),
+ OPT("cdw13", '3', arg_uint32, set_opt, cdw13,
+ "dword 13 value"),
+ OPT("cdw14", '4', arg_uint32, set_opt, cdw14,
+ "dword 14 value"),
+ OPT("cdw15", '5', arg_uint32, set_opt, cdw15,
+ "dword 15 value"),
+ OPT("datafile", 'd', arg_path, set_opt, datafile,
+ "data file for some special features."),
+ OPT_END
+};
+
+static const struct args set_args[] = {
+ { arg_string, &set_opt.dev, "controller-id" },
+ { arg_none, NULL, NULL },
+};
+
+static struct cmd set_cmd = {
+ .name = "set",
+ .fn = feature_set,
+ .descr = "Set Feature for specified device",
+ .ctx_size = sizeof(set_opt),
+ .opts = set_opts,
+ .args = set_args,
+};
+
+CMD_SUBCOMMAND(feature_cmd, set_cmd);
+
+static struct summary_options {
+ const char *dev;
+} summary_opt = {
+ .dev = NULL,
+};
+
+static const struct args summary_args[] = {
+ { arg_string, &summary_opt.dev, "controller-id" },
+ { arg_none, NULL, NULL },
+};
+
+static struct cmd summary_cmd = {
+ .name = "summary",
+ .fn = feature_summary,
+ .descr = "Show Feature summary for specified device",
+ .ctx_size = sizeof(summary_opt),
+ .opts = NULL,
+ .args = summary_args,
+};
+
+CMD_SUBCOMMAND(feature_cmd, summary_cmd);
+
+static SLIST_HEAD(,feature_function) features;
+
+static int
+feature_compare(struct feature_function *a, struct feature_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->fid - (int)b->fid);
+}
+
+void
+feature_register(struct feature_function *p)
+{
+ struct feature_function *l, *a;
+
+ a = NULL;
+ l = SLIST_FIRST(&features);
+ while (l != NULL) {
+ if (feature_compare(l, p) > 0)
+ break;
+ a = l;
+ l = SLIST_NEXT(l, link);
+ }
+ if (a == NULL)
+ SLIST_INSERT_HEAD(&features, p, link);
+ else
+ SLIST_INSERT_AFTER(a, p, link);
+}
+
+static void
+print_feature_arbitration(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Arbitration Burst (AB): ");
+ if ((NVME_FEAT_ARBITRATION_AB(cdw0)) == NVME_FEAT_ARBITRATION_AB_MASK)
+ printf("no limit\n");
+ else
+ printf("%d\n", 1 << (NVME_FEAT_ARBITRATION_AB(cdw0)));
+ printf(" Low Priority Weight (LPW): %d\n",
+ ONE_BASED(NVME_FEAT_ARBITRATION_LPW(cdw0)));
+ printf(" Medium Priority Weight (MPW): %d\n",
+ ONE_BASED(NVME_FEAT_ARBITRATION_MPW(cdw0)));
+ printf(" High Priority Weight (HPW): %d\n",
+ ONE_BASED(NVME_FEAT_ARBITRATION_HPW(cdw0)));
+}
+
+static void
+print_feature_power_management(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Workload Hint (WH): %#x\n",
+ NVME_FEAT_POWER_MANAGEMENT_WH(cdw0));
+ printf(" Power State (PS): %#x\n",
+ NVME_FEAT_POWER_MANAGEMENT_PS(cdw0));
+}
+
+static void
+print_feature_lba_range_type(uint32_t cdw0 __unused, void *buf, uint32_t len)
+{
+ struct nvme_lba_range_type *lba = (struct nvme_lba_range_type*)buf;
+ size_t num;
+ size_t i, j;
+ uint8_t idx;
+
+ num = MIN(ONE_BASED(NVME_FEAT_LBA_RANGE_TYPE_NUM(cdw0)),
+ len / sizeof(struct nvme_lba_range_type));
+
+ for (i = 0; i < num; i++) {
+ if (i == 0 )
+ printf("IDX\tTYPE\t\tSLBA\tNLB\t\tGUID\n");
+ if (idx > lba_range_type_name_res_max)
+ idx = 0;
+ printf("0x%04zx %s", i, lba_range_type_name_res[idx]);
+ printf("\t0x%lx\t0x%08lx\t", lba->slba, ONE_BASED(lba->nlb));
+ for (j = 0; j < sizeof(lba->guid); j++)
+ printf("%02x", lba->guid[j]);
+ printf("\n");
+ lba++;
+ }
+}
+
+static void
+print_feature_temperature_threshold(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Temperature Threshold (TMPTH): ");
+ print_temp(NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH(cdw0));
+}
+
+static void
+print_feature_error_recovery(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Deallocated or Unwritten Logical Block Error Enable (DULBE): %d\n",
+ NVME_FEAT_ERROR_RECOVERY_DULBE(cdw0));
+ printf(" Time Limited Error Recovery (TLER): %d of 100ms\n",
+ NVME_FEAT_ERROR_RECOVERY_TLER(cdw0));
+}
+
+static void
+print_feature_volatile_write_cache(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Volatile Write Cache Enable (WCE): %d\n",
+ NVME_FEAT_VOLATILE_WRITE_CACHE_WCE(cdw0));
+}
+
+static void
+print_feature_number_of_queues(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Number of I/O Completion Queues Allocated (NCQA): %d\n",
+ ONE_BASED(NVME_FEAT_NUMBER_OF_QUEUES_NCQA(cdw0)));
+ printf(" Number of I/O Submission Queues Allocated (NSQA): %d\n",
+ ONE_BASED(NVME_FEAT_NUMBER_OF_QUEUES_NSQA(cdw0)));
+}
+
+static void
+print_feature_interrupt_coalescing(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Aggregation Time (TIME): %d\n",
+ NVME_FEAT_INTERRUPT_COALESCING_TIME(cdw0));
+ printf(" Aggregation Threshold (THR): %d\n",
+ NVME_FEAT_INTERRUPT_COALESCING_THR(cdw0));
+}
+
+static void
+print_feature_interrupt_vector_configuration(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Coalescing Disable (CD): %d\n",
+ NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_CD(cdw0));
+ printf(" Interrupt Vector (IV): %d\n",
+ NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_IV(cdw0));
+}
+
+static void
+print_feature_write_atomicity(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Disable Normal (DN): %d\n",
+ NVME_FEAT_WRITE_ATOMICITY_DN(cdw0));
+}
+
+static void
+print_feature_async_event_configuration(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Telemetry Log Notices: %d\n",
+ NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN(cdw0));
+ printf(" Firmware Activation Notices: %d\n",
+ NVME_FEAT_ASYNC_EVENT_CONFIGURATION_FAN(cdw0));
+ printf(" Namespace Attribute Notices: %d\n",
+ NVME_FEAT_ASYNC_EVENT_CONFIGURATION_NAN(cdw0));
+ printf(" SMART/Health Critical Warnings: %d\n",
+ NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN(cdw0));
+}
+
+static void
+print_feature_autonomous_power_state_transition(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Autonomous Power State Transition Enable (APSTE): %d\n",
+ NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION_APSTE(cdw0));
+}
+
+static void
+print_feature_timestamp(uint32_t cdw0 __unused, void *buf, uint32_t len)
+{
+ uint8_t *data = (uint8_t*)buf;
+ uint64_t milliseconds, seconds;
+ uint8_t origin;
+ bool synch;
+ uint32_t days, hours, minutes;
+
+ if (len < 8)
+ return;
+
+ origin = (data[6] >> 1) & 0x07;
+ synch = data[6] & 0x01;
+
+ memcpy((uint8_t*)&milliseconds, (uint8_t*)data, len);
+ milliseconds &= 0xffffffffffff;
+ seconds = milliseconds / 1000;
+ minutes = seconds / 60;
+ hours = minutes / 60;
+ days = hours / 24;
+
+ switch (origin) {
+ case 0x0:
+ printf(" Origin: since controller reset\n");
+ break;
+ case 0x1:
+ printf(" Origin: since last set feature\n");
+ break;
+ default:
+ printf(" Origin: reserved\n" );
+ break;
+ }
+ if (synch)
+ printf(" Synch: may not continuously.\n");
+ else
+ printf(" Synch: continuously\n");
+
+ printf(" Time elapsed: %lu ms,", milliseconds);
+ printf(" (%d days %02d:%02d:%02lu.%03lu)\n",
+ days, hours % 24, minutes % 60, seconds % 60, milliseconds % 1000);
+}
+
+static void
+print_feature_keep_alive_timer(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Keep Alive Timeout (KATO): %d\n",
+ NVME_FEAT_KEEP_ALIVE_TIMER_KATO(cdw0));
+}
+
+static void
+print_feature_host_memory_buffer(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Memory Return (MR): %d\n",
+ NVME_FEAT_HOST_MEMORY_BUFFER_MR(cdw0));
+ printf(" Enable Host Memory (EHM): %d\n",
+ NVME_FEAT_HOST_MEMORY_BUFFER_EHM(cdw0));
+}
+
+static void
+print_feature_host_controlled_thermal_mgmt(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Thermal Management Temperature 1 (TMT1): %d\n",
+ NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT1(cdw0));
+ printf(" Thermal Management Temperature 2 (TMT2): %d\n",
+ NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT2(cdw0));
+}
+
+static void
+print_feature_non_op_power_state_config(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Non-Operational Power State Permissive Mode Enable (NOPPME): %d\n",
+ NVME_FEAT_NON_OP_POWER_STATE_CONFIG_NOPPME(cdw0));
+}
+
+void
+get_feature_not_support(uint32_t cdw0 __unused, void *buf __unused, uint32_t len __unused)
+{
+
+ printf("Get for this feature is not support\n");
+}
+
+static void
+print_feat_hex(uint32_t cdw0, void *buf, uint32_t len)
+{
+
+ if (buf != NULL && len > 0)
+ print_hex(buf, len);
+ else
+ printf("0x%X\n", cdw0);
+}
+
+static void
+read_data_file(const char *path, void **buf, int32_t *size)
+{
+ struct stat sb;
+ int32_t filesize;
+ int fd;
+
+ *size = 0;
+ *buf = NULL;
+
+ if ((fd = open(path, O_RDONLY)) < 0)
+ err(EX_NOINPUT, "unable to open '%s'", path);
+ if (fstat(fd, &sb) < 0)
+ err(EX_NOINPUT, "unable to stat '%s'", path);
+
+ if (sb.st_size > INT32_MAX)
+ errx(EX_USAGE, "size of file '%s' is too large (%jd bytes)",
+ path, (intmax_t)sb.st_size);
+ filesize = (int32_t)sb.st_size;
+ if ((*buf = malloc(filesize)) == NULL)
+ errx(EX_OSERR, "unable to malloc %d bytes", filesize);
+ if ((*size = read(fd, *buf, filesize)) < 0)
+ err(EX_IOERR, "error reading '%s'", path);
+ /* XXX assuming no short reads */
+ if (*size != filesize)
+ errx(EX_IOERR,
+ "error reading '%s' (read %d bytes, requested %d bytes)",
+ path, *size, filesize);
+ close(fd);
+}
+
+static void
+feature_help(void)
+{
+ const struct feature_function *f;
+ const char *v;
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "%-10s %-10s %s\n", "Feature Id", "Vendor","Feature Name");
+ fprintf(stderr, "---------- ---------- ----------\n");
+ SLIST_FOREACH(f, &features, link) {
+ v = f->vendor == NULL ? "-" : f->vendor;
+ fprintf(stderr, "0x%02x %-10s %s\n", f->fid, v, f->name);
+ }
+
+ exit(EX_USAGE);
+}
+
+static void
+interpret_nvme_feature_set_status(uint8_t status)
+{
+
+ switch (status) {
+ case 0x0d:
+ printf("Feature Identifier Not Saveable\n");
+ break;
+ case 0x0e:
+ printf("Feature Not Changeable\n");
+ break;
+ case 0x0f:
+ printf("Feature Not Namesapce Specific\n");
+ break;
+ case 0x14:
+ printf("Overlapping Range\n");
+ break;
+ default:
+ printf("Unknown Status Code: %u\n", status);
+ break;
+ }
+}
+
+static void
+feature_get(const struct cmd *f, int argc, char *argv[])
+{
+ struct nvme_pt_command pt;
+ int fd;
+ const struct feature_function *ff;
+ feat_print_fn_t print_fn;
+ uint8_t *buf = NULL;
+ uint32_t size = 0;
+
+ if (arg_parse(argc, argv, f))
+ return;
+ if (get_opt.vendor != NULL && strcmp(get_opt.vendor, "help") == 0)
+ feature_help();
+ if (get_opt.fid == NONE) {
+ fprintf(stderr, "Missing feature id (-f).\n");
+ arg_help(argc, argv, f);
+ }
+
+ print_fn = print_feat_hex;
+ size = 0;
+ if (!get_opt.hex) {
+ /*
+ * See if there is a pretty print function for the specified
+ * feature. If one isn't found, we just revert to the default
+ * (print_hex). If there was a vendor specified by the user, and
+ * the page is vendor specific, don't match the print function
+ * unless the vendors match.
+ */
+ SLIST_FOREACH(ff, &features, link) {
+ if (ff->vendor != NULL && get_opt.vendor != NULL &&
+ strcmp(ff->vendor, get_opt.vendor) != 0)
+ continue;
+ if (get_opt.fid != ff->fid)
+ continue;
+ if (ff->print_fn != NULL)
+ print_fn = ff->print_fn;
+ size = ff->size;
+ break;
+ }
+ }
+
+ open_dev(get_opt.dev, &fd, 1, 1);
+
+ memset(&pt, 0, sizeof(pt));
+ pt.cmd.opc = NVME_OPC_GET_FEATURES;
+ pt.cmd.cdw10 = htole32((get_opt.sel << NVME_FEAT_GET_SEL_SHIFT) |
+ (get_opt.fid << NVME_FEAT_GET_FID_SHIFT));
+ pt.cmd.cdw11 = htole32(get_opt.cdw11);
+ pt.buf = buf;
+ pt.len = size;
+ pt.is_read = 1;
+
+ if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
+ err(EX_IOERR, "passthrough request failed");
+
+ if (nvme_completion_is_error(&pt.cpl))
+ errx(EX_IOERR, "get feature request returned error");
+
+ print_fn(pt.cpl.cdw0, buf, size);
+
+ close(fd);
+ exit(0);
+}
+
+static void
+feature_set(const struct cmd *f, int argc, char *argv[])
+{
+ struct nvme_pt_command pt;
+ const struct feature_function *ff;
+ int fd;
+ void *buf = NULL;
+ feat_interpret_fn_t pret_fn;
+ int32_t size = 0;
+
+ if (arg_parse(argc, argv, f))
+ return;
+ if (set_opt.vendor != NULL && strcmp(set_opt.vendor, "help") == 0)
+ feature_help();
+ if (set_opt.fid == NONE) {
+ fprintf(stderr,
+ "feature id not specified\n");
+ arg_help(argc, argv, f);
+ }
+
+ if (set_opt.datafile)
+ read_data_file(set_opt.datafile, &buf, &size);
+
+ if (set_opt.fid == NVME_FEAT_LBA_RANGE_TYPE &&
+ ( size > 4096 || size % 64 != 0)) {
+ errx(EX_USAGE, "data size of file '%s' is too large (%d bytes) or not align to 64 bytes",
+ set_opt.datafile, size);
+ }
+
+ pret_fn = NULL;
+ SLIST_FOREACH(ff, &features, link) {
+ if (ff->vendor != NULL && get_opt.vendor != NULL &&
+ strcmp(ff->vendor, set_opt.vendor) != 0)
+ continue;
+ if (set_opt.fid != ff->fid)
+ continue;
+ if (ff->pret_fn != NULL)
+ pret_fn = ff->pret_fn;
+ size = ff->size;
+ break;
+ }
+
+ open_dev(set_opt.dev, &fd, 1, 1);
+
+ memset(&pt, 0, sizeof(pt));
+ pt.cmd.opc = NVME_OPC_SET_FEATURES;
+ pt.cmd.cdw10 = htole32((set_opt.save << NVME_FEAT_SET_SV_SHIFT) |
+ (set_opt.fid << NVME_FEAT_SET_FID_SHIFT));
+ pt.cmd.cdw11 = htole32(set_opt.cdw11);
+ pt.cmd.cdw12 = htole32(set_opt.cdw12);
+ pt.cmd.cdw13 = htole32(set_opt.cdw13);
+ pt.cmd.cdw14 = htole32(set_opt.cdw14);
+ pt.cmd.cdw15 = htole32(set_opt.cdw15);
+ pt.buf = buf;
+ pt.len = size;
+ pt.is_read = 0;
+
+ if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
+ err(EX_IOERR, "passthrough request failed");
+
+ if (nvme_completion_is_error(&pt.cpl)) {
+ if (NVME_STATUS_GET_SCT(pt.cpl.status) == NVME_SCT_COMMAND_SPECIFIC && pret_fn) {
+ pret_fn(NVME_STATUS_GET_SC(pt.cpl.status));
+ }
+ else
+ errx(EX_IOERR, "set feature request returned error");
+ }
+ else
+ printf("set feature success\n");
+
+ close(fd);
+ exit(0);
+}
+
+static void
+feature_summary(const struct cmd *f, int argc, char *argv[])
+{
+ struct nvme_pt_command pt;
+ const struct feature_function *ff;
+ uint8_t fid, sel;
+ uint32_t cdw11;
+ int fd;
+ feat_print_fn_t print_fn;
+ uint8_t *buf = NULL;
+ uint32_t i;
+ uint32_t size = 0;
+
+ if (arg_parse(argc, argv, f))
+ return;
+
+ open_dev(summary_opt.dev, &fd, 1, 1);
+
+ for (i = 0; i < feature_summary_list_max; i++) {
+ fid = feature_summary_list[i].fid;
+ sel = feature_summary_list[i].sel;
+ cdw11 = feature_summary_list[i].cdw11;
+
+ memset(&pt, 0, sizeof(pt));
+ pt.cmd.opc = NVME_OPC_GET_FEATURES;
+ pt.cmd.cdw10 = htole32((sel << NVME_FEAT_GET_SEL_SHIFT) |
+ (fid << NVME_FEAT_GET_FID_SHIFT));
+ pt.cmd.cdw11 = htole32(cdw11);
+ pt.buf = buf;
+ pt.len = size;
+ pt.is_read = 1;
+
+ if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
+ continue;
+
+ if (nvme_completion_is_error(&pt.cpl))
+ continue;
+
+ print_fn = print_feat_hex;
+ SLIST_FOREACH(ff, &features, link) {
+ if (fid != ff->fid)
+ continue;
+ if (ff->print_fn != NULL)
+ print_fn = ff->print_fn;
+ size = ff->size;
+ break;
+ }
+
+ print_fn(pt.cpl.cdw0, buf, size);
+ }
+
+ close(fd);
+ exit(0);
+}
+
+static void
+feature(const struct cmd *nf __unused, int argc, char *argv[])
+{
+
+ cmd_dispatch(argc, argv, &feature_cmd);
+}
+
+NVME_FEATURE(arbitration,
+ NVME_FEAT_ARBITRATION, NULL, "Command Arbitration",
+ print_feature_arbitration, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(power_management,
+ NVME_FEAT_POWER_MANAGEMENT, NULL, "Power Management",
+ print_feature_power_management, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(lba_range_type,
+ NVME_FEAT_LBA_RANGE_TYPE, NULL, "LBA Range Type",
+ print_feature_lba_range_type, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(temperature_threshold,
+ NVME_FEAT_TEMPERATURE_THRESHOLD, NULL, "Temperature Threshold",
+ print_feature_temperature_threshold, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(error_recovery,
+ NVME_FEAT_ERROR_RECOVERY, NULL, "Error Recovery",
+ print_feature_error_recovery, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(volatile_write_cache,
+ NVME_FEAT_VOLATILE_WRITE_CACHE, NULL, "Volatile Write Cache",
+ print_feature_volatile_write_cache, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(number_of_queues,
+ NVME_FEAT_NUMBER_OF_QUEUES, NULL, "Number of Queues",
+ print_feature_number_of_queues, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(interrupt_coalescing,
+ NVME_FEAT_INTERRUPT_COALESCING, NULL, "Interrupt Coalescing",
+ print_feature_interrupt_coalescing, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(interrupt_vector_configuration,
+ NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION, NULL, "Interrupt Vector Configuration",
+ print_feature_interrupt_vector_configuration, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(write_atomicity,
+ NVME_FEAT_WRITE_ATOMICITY, NULL, "Write Atomicity Normal",
+ print_feature_write_atomicity, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(async_event_configuration,
+ NVME_FEAT_ASYNC_EVENT_CONFIGURATION, NULL, "Asynchronous Event Configuration",
+ print_feature_async_event_configuration, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(autonomous_power_state_transition,
+ NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION, NULL, "Autonomous Power State Transition",
+ print_feature_autonomous_power_state_transition, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(host_memory_buffer,
+ NVME_FEAT_HOST_MEMORY_BUFFER, NULL, "Host Memory Buffer",
+ print_feature_host_memory_buffer, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(timestamp,
+ NVME_FEAT_TIMESTAMP, NULL, "Timestamp",
+ print_feature_timestamp, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(keep_alive_timer,
+ NVME_FEAT_KEEP_ALIVE_TIMER, NULL, "Keep Alive Timer",
+ print_feature_keep_alive_timer, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(host_controlled_thermal_mgmt,
+ NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT, NULL, "Host Controlled Thermal Management",
+ print_feature_host_controlled_thermal_mgmt, interpret_nvme_feature_set_status, 0);
+NVME_FEATURE(non_op_power_state_config,
+ NVME_FEAT_NON_OP_POWER_STATE_CONFIG, NULL, "Non-Operational Power State Config",
+ print_feature_non_op_power_state_config, interpret_nvme_feature_set_status, 0);
Index: sbin/nvmecontrol/nvmecontrol.h
===================================================================
--- sbin/nvmecontrol/nvmecontrol.h
+++ sbin/nvmecontrol/nvmecontrol.h
@@ -35,6 +35,8 @@
#include "comnd.h"
typedef void (*print_fn_t)(const struct nvme_controller_data *cdata, void *buf, uint32_t size);
+typedef void (*feat_print_fn_t)(uint32_t cdw0, void *buf, uint32_t size);
+typedef void (*feat_interpret_fn_t)(uint8_t status);
struct logpage_function {
SLIST_ENTRY(logpage_function) link;
@@ -45,6 +47,16 @@
size_t size;
};
+struct feature_function {
+ SLIST_ENTRY(feature_function) link;
+ uint8_t fid;
+ const char *vendor;
+ const char *name;
+ feat_print_fn_t print_fn;
+ feat_interpret_fn_t pret_fn;
+ size_t size;
+};
+
#define NVME_LOGPAGE(unique, lp, vend, nam, fn, sz) \
static struct logpage_function unique ## _lpf = { \
.log_page = lp, \
@@ -56,6 +68,18 @@
static void logpage_reg_##unique(void) __attribute__((constructor)); \
static void logpage_reg_##unique(void) { logpage_register(&unique##_lpf); }
+#define NVME_FEATURE(unique, id, vend, na, fnp, fni, sz) \
+ static struct feature_function unique ## _ff = { \
+ .fid = id, \
+ .vendor = vend, \
+ .name = na, \
+ .print_fn = fnp, \
+ .pret_fn = fni, \
+ .size = sz, \
+ } ; \
+ static void feature_reg_##unique(void) __attribute__((constructor)); \
+ static void feature_reg_##unique(void) { feature_register(&unique##_ff); }
+
#define DEFAULT_SIZE (4096)
struct kv_name {
uint32_t key;
@@ -65,6 +89,8 @@
const char *kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key);
void logpage_register(struct logpage_function *p);
+void feature_register(struct feature_function *p);
+void get_feature_not_support(uint32_t cdw0, void *buf, uint32_t len);
#define NVME_CTRLR_PREFIX "nvme"
#define NVME_NS_PREFIX "ns"
Index: sbin/nvmecontrol/nvmecontrol.8
===================================================================
--- sbin/nvmecontrol/nvmecontrol.8
+++ sbin/nvmecontrol/nvmecontrol.8
@@ -147,6 +147,29 @@
.Op Fl x
.Aq Ar namespace-id
.Nm
+.Ic feature get
+.Aq Fl f Ar feature-id
+.Op Fl v Ar vendor-string
+.Op Fl s Ar select
+.Op Fl x
+.Op Fl 1 Ar value
+.Aq Ar device-id
+.Nm
+.Ic feature set
+.Aq Fl f Ar feature-id
+.Op Fl v Ar vendor-string
+.Op Fl s Ar save
+.Op Fl 1 Ar value
+.Op Fl 2 Ar value
+.Op Fl 3 Ar value
+.Op Fl 4 Ar value
+.Op Fl 5 Ar value
+.Op Fl d Ar path_to_datafile
+.Aq Ar device-id
+.Nm
+.Ic feature summary
+.Aq Ar device-id
+.Nm
.Ic firmware
.Op Fl s Ar slot
.Op Fl f Ar path_to_firmware
@@ -242,6 +265,87 @@
.Dv IDENTIFY_CONTROLLER
data associated with that drive.
.El
+.Ss feature
+The feature command includes
+.Ic get set
+and
+.Ic summary
+sub commands
+to get and set a feature for specific device.
+It also knows about vendor specific feature.
+Specifying help as vendor string will list all valid vendors and features
+.El
+.Ss feature get
+The feature get command can output feature settings for specific device.
+.Bl -tag -width 8n
+.It Fl f Ar feature-id
+Feature id to get
+.It Fl v
+vendor-string
+.It Fl s
+Select
+.Bl -tag -compact -width 6n
+.It Dv 0
+Current
+.It Dv 1
+Default
+.It Dv 2
+Saved
+.It Dv 3
+Supported capabilities
+.El
+.It Fl x
+Print data as hex
+.It Fl 1 Ar value
+32-bit value for CDW11.
+.El
+.Ss feature set
+The feature set command can set feature for specific device.
+.Bl -tag -width 8n
+.It Fl f Ar feature-id
+Feature id to set
+.It Fl v
+vendor-string
+.It Fl s
+Requests that the controller save the attribute
+so that the attribute persists through all power states and resets.
+.It Fl 1 Ar value
+32-bit value for CDW11.
+.It Fl 2 Ar value
+32-bit value for CDW12.
+.It Fl 3 Ar value
+32-bit value for CDW13.
+.It Fl 4 Ar value
+32-bit value for CDW14.
+.It Fl 5 Ar value
+32-bit value for CDW15.
+.It Fl f Ar path_to_datafile
+Data file for specific feature.
+.El
+.Ss feature summary
+The feature summary command will output all mandatory features
+for specific device. These features are:
+.Pp
+.Bl -tag -compact -width "Fid 0x00"
+.It Dv Fid 0x01
+Command Arbitration
+.It Dv Fid 0x02
+Power Management
+.It Dv Fid 0x04
+Temperature Threshold
+.It Dv Fid 0x05
+Error Recovery
+.It Dv Fid 0x07
+Number of Queues
+.It Dv Fid 0x08
+Interrupt Coalescing
+.It Dv Fid 0x09
+Interrupt Vector Configuration
+.It Dv Fid 0x0A
+Write Atomicity Normal
+.It Dv Fid 0x0B
+Asynchronous Event Configuration
+.El
.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.
@@ -670,6 +774,15 @@
standard out.
Redirect it to a temporary file.
.Pp
+.Dl nvmecontrol feature get -f0x01 -s1 nvme0
+.Pp
+Get feature default value of Command Arbitration for nvme0 controller.
+.Pp
+.Dl nvmecontrol feature set -f0x04 -s -10x157 nvme0
+.Pp
+Set feature value of Temperature Threshold for nvme0 controller,
+and tell controller to save that value.
+.Pp
.Dl nvmecontrol firmware -s 2 -f /tmp/nvme_firmware nvme0
.Pp
Download the firmware image contained in "/tmp/nvme_firmware" to slot 2 of the
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Feb 8, 5:01 AM (12 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28465493
Default Alt Text
D32700.id101343.diff (29 KB)
Attached To
Mode
D32700: nvmecontrol: Implement Get/Set Feature for nvmecontrol
Attached
Detach File
Event Timeline
Log In to Comment