Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F145182992
D32700.id100389.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.id100389.diff
View Options
Index: sbin/nvmecontrol/feature.c
===================================================================
--- /dev/null
+++ sbin/nvmecontrol/feature.c
@@ -0,0 +1,837 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (C) 2021 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", 'h', arg_none, get_opt, hex,
+ "print result hex"),
+ OPT("fid", 'f', arg_uint8, get_opt, fid,
+ "feature id (required)"),
+ 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("vendor", 'v', arg_string, get_opt, vendor,
+ "Vendor specific formatting"),
+ 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("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("vendor", 'v', arg_string, set_opt, vendor,
+ "Vendor specific formatting"),
+ OPT("datafile", 'f', 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 miliseconds, 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*)&miliseconds, (uint8_t*)data, len);
+ miliseconds &= 0xffffffffffff;
+ seconds = miliseconds / 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,", miliseconds);
+ printf(" (%d days %02d:%02d:%02lu.%03lu)\n",
+ days, hours % 24, minutes % 60, seconds % 60, miliseconds % 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.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 cdw0, 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;
+
+ cdw0 = pt.cpl.cdw0;
+
+ 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/modules/intel/intel.c
===================================================================
--- sbin/nvmecontrol/modules/intel/intel.c
+++ sbin/nvmecontrol/modules/intel/intel.c
@@ -181,6 +181,97 @@
}
}
+static void
+print_intel_max_lba(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Max LBA: ");
+ printf("%u\n", cdw0);
+}
+
+static void
+print_intel_native_max_lba(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+
+ printf(" Native Max LBA: ");
+ printf("%u\n", cdw0);
+}
+
+static void
+print_intel_power_governor_setting(uint32_t cdw0, void *buf __unused, uint32_t len __unused)
+{
+ uint8_t pg = cdw0 & 0xff; /* Power Governor */
+
+ printf(" Power Governor Setting (PGS): ");
+ switch (pg) {
+ case 0x00:
+ printf("25W(typ)\n");
+ break;
+ case 0x01:
+ printf("20W(typ)\n");
+ break;
+ case 0x02:
+ printf("10W(typ)\n");
+ break;
+ default:
+ printf("%d\n", pg);
+ break;
+ }
+}
+
+static void
+interpret_intel_max_lba(uint8_t status)
+{
+
+ switch (status) {
+ case 0xc0:
+ printf("Requested MAX LBA exceeds Available capacity\n");
+ break;
+ case 0xc1:
+ printf("Requested MAX LBA smaller than minimum allowable limit\n");
+ break;
+ case 0xc2:
+ printf("Requested MAX LBA is smaller than allocated Namespace requirements\n");
+ break;
+ default:
+ printf("Unknown Status Code: %u\n", status);
+ break;
+ }
+}
+
+static void
+interpret_intel_power_governor_setting(uint8_t status)
+{
+
+ switch (status) {
+ case 0xc0:
+ printf("Invalid Setting\n");
+ break;
+ default:
+ printf("Unknown Status Code: %u\n", status);
+ break;
+ }
+}
+
+static void
+interpret_intel_reset_timed_workload_counters(uint8_t status)
+{
+
+ switch (status) {
+ case 0xc0:
+ printf("Invalid Setting\n");
+ break;
+ default:
+ printf("Unknown Status Code: %u\n", status);
+ break;
+ }
+}
+
+#define INTEL_FEAT_MAX_LBA 0xc1
+#define INTEL_FEAT_NATIVE_MAX_LBA 0xc2
+#define INTEL_FEAT_POWER_GOVERNOR_SETTING 0xc6
+#define INTEL_FEAT_RESET_TIMED_WORKLOAD_COUNTERS 0xd5
+
NVME_LOGPAGE(intel_temp,
INTEL_LOG_TEMP_STATS, "intel", "Temperature Stats",
print_intel_temp_stats, sizeof(struct intel_log_temp_stats));
@@ -193,3 +284,15 @@
NVME_LOGPAGE(intel_smart,
INTEL_LOG_ADD_SMART, "intel", "Extra Health/SMART Data",
print_intel_add_smart, DEFAULT_SIZE);
+NVME_FEATURE(intel_max_lba,
+ INTEL_FEAT_MAX_LBA, "intel", "Max LBA",
+ print_intel_max_lba, interpret_intel_max_lba, 0);
+NVME_FEATURE(intel_native_max_lba,
+ INTEL_FEAT_NATIVE_MAX_LBA, "intel", "Native Max LBA",
+ print_intel_native_max_lba, NULL, 0);
+NVME_FEATURE(intel_power_governor_setting,
+ INTEL_FEAT_POWER_GOVERNOR_SETTING, "intel", "Power Governor Setting",
+ print_intel_power_governor_setting, interpret_intel_power_governor_setting, 0);
+NVME_FEATURE(intel_reset_timed_workload_counters,
+ INTEL_FEAT_RESET_TIMED_WORKLOAD_COUNTERS, "intel", "Reset Timed Workload Counters",
+ get_feature_not_support, interpret_intel_reset_timed_workload_counters, 0);
Index: sbin/nvmecontrol/nvmecontrol.h
===================================================================
--- sbin/nvmecontrol/nvmecontrol.h
+++ sbin/nvmecontrol/nvmecontrol.h
@@ -35,26 +35,50 @@
#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;
+ SLIST_ENTRY(logpage_function) link;
uint8_t log_page;
- const char *vendor;
- const char *name;
+ const char *vendor;
+ const char *name;
print_fn_t print_fn;
size_t size;
};
-#define NVME_LOGPAGE(unique, lp, vend, nam, fn, sz) \
+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, na, fn, sz) \
static struct logpage_function unique ## _lpf = { \
.log_page = lp, \
.vendor = vend, \
- .name = nam, \
+ .name = na, \
.print_fn = fn, \
.size = sz, \
} ; \
- static void logpage_reg_##unique(void) __attribute__((constructor)); \
- static void logpage_reg_##unique(void) { logpage_register(&unique##_lpf); }
+ 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 {
@@ -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"
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Feb 17, 8:51 PM (14 h, 26 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28824451
Default Alt Text
D32700.id100389.diff (29 KB)
Attached To
Mode
D32700: nvmecontrol: Implement Get/Set Feature for nvmecontrol
Attached
Detach File
Event Timeline
Log In to Comment