Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F132994472
D32700.id97992.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
31 KB
Referenced Files
None
Subscribers
None
D32700.id97992.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,845 @@
+/*-
+ * 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 <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 NONE8 0xffu
+#define NONE32 0xffffffffu
+#define NONE64 0xffffffffffffffffull
+#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)
+
+#define NVME_FEAT_GET_SEL_SHIFT (8)
+#define NVME_FEAT_GET_SEL_MASK (0x7)
+#define NVME_FEAT_GET_FID_SHIFT (0)
+#define NVME_FEAT_GET_FID_MASK (0xFF)
+
+#define NVME_FEAT_SET_SV_SHIFT (31)
+#define NVME_FEAT_SET_SV_MASK (0x1)
+#define NVME_FEAT_SET_FID_SHIFT (0)
+#define NVME_FEAT_SET_FID_MASK (0xFF)
+
+#define NVME_FEAT_ARBITRATION_HPW_SHIFT (24)
+#define NVME_FEAT_ARBITRATION_HPW_MASK (0xFF)
+#define NVME_FEAT_ARBITRATION_MPW_SHIFT (16)
+#define NVME_FEAT_ARBITRATION_MPW_MASK (0xFF)
+#define NVME_FEAT_ARBITRATION_LPW_SHIFT (8)
+#define NVME_FEAT_ARBITRATION_LPW_MASK (0xFF)
+#define NVME_FEAT_ARBITRATION_AB_SHIFT (0)
+#define NVME_FEAT_ARBITRATION_AB_MASK (0x7)
+
+#define NVME_FEAT_POWER_MANAGEMENT_WH_SHIFT (5)
+#define NVME_FEAT_POWER_MANAGEMENT_WH_MASK (0x7)
+#define NVME_FEAT_POWER_MANAGEMENT_PS_SHIFT (0)
+#define NVME_FEAT_POWER_MANAGEMENT_PS_MASK (0x1F)
+
+#define NVME_FEAT_LBA_RANGE_TYPE_NUM_SHIFT (0)
+#define NVME_FEAT_LBA_RANGE_TYPE_NUM_MASK (0x1F)
+
+#define NVME_FEAT_TEMPERATURE_THRESHOLD_THSEL_SHIFT (20)
+#define NVME_FEAT_TEMPERATURE_THRESHOLD_THSEL_MASK (0x3)
+#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPSEL_SHIFT (16)
+#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPSEL_MASK (0xF)
+#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_SHIFT (0)
+#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_MASK (0xFFFF)
+
+#define NVME_FEAT_ERROR_RECOVERY_DULBE_SHIFT (16)
+#define NVME_FEAT_ERROR_RECOVERY_DULBE_MASK (0x1)
+#define NVME_FEAT_ERROR_RECOVERY_TLER_SHIFT (0)
+#define NVME_FEAT_ERROR_RECOVERY_TLER_MASK (0xFF)
+
+#define NVME_FEAT_VOLATILE_WRITE_CACHE_WCE_SHIFT (0)
+#define NVME_FEAT_VOLATILE_WRITE_CACHE_WCE_MASK (0x1)
+
+#define NVME_FEAT_NUMBER_OF_QUEUES_NCQA_SHIFT (16)
+#define NVME_FEAT_NUMBER_OF_QUEUES_NCQA_MASK (0xFFFF)
+#define NVME_FEAT_NUMBER_OF_QUEUES_NSQA_SHIFT (0)
+#define NVME_FEAT_NUMBER_OF_QUEUES_NSQA_MASK (0xFFFF)
+
+#define NVME_FEAT_INTERRUPT_COALESCING_TIME_SHIFT (8)
+#define NVME_FEAT_INTERRUPT_COALESCING_TIME_MASK (0xFF)
+#define NVME_FEAT_INTERRUPT_COALESCING_THR_SHIFT (0)
+#define NVME_FEAT_INTERRUPT_COALESCING_THR_MASK (0xFF)
+
+#define NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_CD_SHIFT (16)
+#define NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_CD_MASK (0x1)
+#define NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_IV_SHIFT (0)
+#define NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_IV_MASK (0xFF)
+
+#define NVME_FEAT_WRITE_ATOMICITY_DN_SHIFT (0)
+#define NVME_FEAT_WRITE_ATOMICITY_DN_MASK (0x1)
+
+#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN_SHIFT (10)
+#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN_MASK (0x1)
+#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_FAN_SHIFT (9)
+#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_FAN_MASK (0x1)
+#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_NAN_SHIFT (8)
+#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_NAN_MASK (0x1)
+#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_HCW_SHIFT (0)
+#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_HCW_MASK (0xFF)
+
+#define NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION_APSTE_SHIFT (0)
+#define NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION_APSTE_MASK (0x1)
+
+#define NVME_FEAT_HOST_MEMORY_BUFFER_MR_SHIFT (1)
+#define NVME_FEAT_HOST_MEMORY_BUFFER_MR_MASK (0x1)
+#define NVME_FEAT_HOST_MEMORY_BUFFER_EHM_SHIFT (0)
+#define NVME_FEAT_HOST_MEMORY_BUFFER_EHM_MASK (0x1)
+#define NVME_FEAT_HOST_MEMORY_BUFFER_HSIZE_SHIFT (0)
+#define NVME_FEAT_HOST_MEMORY_BUFFER_HSIZE_MASK (0xFFFF)
+#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLLA_SHIFT (4)
+#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLLA_MASK (0xFFF)
+#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLUA_SHIFT (0)
+#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLUA_MASK (0xFFFF)
+#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLEC_SHIFT (0)
+#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLEC_MASK (0xFFFF)
+
+#define NVME_FEAT_TIMESTAMP_TS_SHIFT (0x0)
+#define NVME_FEAT_TIMESTAMP_TS_MASK (0x3F)
+
+#define NVME_FEAT_KEEP_ALIVE_TIMER_KATO_SHIFT (0x0)
+#define NVME_FEAT_KEEP_ALIVE_TIMER_KATO_MASK (0xFFFF)
+
+#define NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT1_SHIFT (16)
+#define NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT1_MASK (0xFF)
+#define NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT2_SHIFT (0)
+#define NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT2_MASK (0xFF)
+
+#define NVME_FEAT_NON_OP_POWER_STATE_CONFIG_NOPPME_SHIFT (0)
+#define NVME_FEAT_NON_OP_POWER_STATE_CONFIG_NOPPME_MASK (0x1)
+
+static
+const char *bits[16] = {
+ [ 0] = "0000", [ 1] = "0001", [ 2] = "0010", [ 3] = "0011",
+ [ 4] = "0100", [ 5] = "0101", [ 6] = "0110", [ 7] = "0111",
+ [ 8] = "1000", [ 9] = "1001", [10] = "1010", [11] = "1011",
+ [12] = "1100", [13] = "1101", [14] = "1110", [15] = "1111",
+};
+
+static const char *
+feature_name_res[] = {
+ [0] = "Reserved",
+ [1] = "Command Arbitration",
+ [2] = "Power Management",
+ [3] = "LBA Range Type",
+ [4] = "Temperature Threshold",
+ [5] = "Error Recovery",
+ [6] = "Volatile Write Cache",
+ [7] = "Number of Queues",
+ [8] = "Interrupt Coalescing",
+ [9] = "Interrupt Vector Configuration",
+ [10] = "Write Atomicity Normal",
+ [11] = "Asynchronous Event Configuration",
+ [12] = "Autonomous Power State Transition",
+ [13] = "Host Memory Buffer",
+ [14] = "Timestamp",
+ [15] = "Keep Alive Timer",
+ [16] = "Host Controlled Thermal Management",
+ [17] = "Non-Operational Power State Config",
+};
+static uint32_t feature_name_res_max = nitems(feature_name_res);
+
+static const char *
+sel_name_res[] = {
+ [0] = "Current",
+ [1] = "Default",
+ [2] = "Saved",
+ [3] = "Supported capabilities",
+};
+static uint32_t sel_name_res_max = nitems(sel_name_res);
+
+static const char *
+lba_range_type_name_res[] = {
+ [0] = "Reserved",
+ [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 {
+ uint8_t fid;
+ uint8_t sel;
+ uint32_t cdw11;
+ bool binary;
+ const char *dev;
+} get_opt = {
+ .fid = NONE8,
+ .sel = 0,
+ .cdw11 = 0,
+ .binary = false,
+ .dev = NULL,
+};
+
+static const struct opts get_opts[] = {
+ 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("binary", 'b', arg_none, get_opt, binary,
+ "print result binary"),
+ 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 *dev;
+} set_opt = {
+ .fid = NONE8,
+ .save = false,
+ .cdw11 = 0,
+ .cdw12 = 0,
+ .cdw13 = 0,
+ .cdw14 = 0,
+ .cdw15 = 0,
+ .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_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 void
+print_feature_arbitration(uint32_t cdw0)
+{
+
+ printf(" Arbitration Burst (AB): ");
+ if ((cdw0 & NVME_FEAT_ARBITRATION_AB_MASK) == NVME_FEAT_ARBITRATION_AB_MASK)
+ printf("no limit\n");
+ else
+ printf("%d\n", 1 << (cdw0 & NVME_FEAT_ARBITRATION_AB_MASK));
+ printf(" Low Priority Weight (LPW): %d\n",
+ ONE_BASED((cdw0 >> NVME_FEAT_ARBITRATION_LPW_SHIFT) &
+ NVME_FEAT_ARBITRATION_LPW_MASK));
+ printf(" Medium Priority Weight (MPW): %d\n",
+ ONE_BASED((cdw0 >> NVME_FEAT_ARBITRATION_MPW_SHIFT) &
+ NVME_FEAT_ARBITRATION_MPW_MASK));
+ printf(" High Priority Weight (HPW): %d\n",
+ ONE_BASED((cdw0 >> NVME_FEAT_ARBITRATION_HPW_SHIFT) &
+ NVME_FEAT_ARBITRATION_HPW_MASK));
+}
+
+static void
+print_feature_power_management(uint32_t cdw0)
+{
+
+ printf(" Workload Hint (WH): %#x\n",
+ (cdw0 >> NVME_FEAT_POWER_MANAGEMENT_WH_SHIFT) &
+ NVME_FEAT_POWER_MANAGEMENT_WH_MASK);
+ printf(" Power State (PS): %#x\n",
+ (cdw0 >> NVME_FEAT_POWER_MANAGEMENT_PS_SHIFT) &
+ NVME_FEAT_POWER_MANAGEMENT_PS_MASK);
+}
+
+static void
+print_feature_lba_range_type(uint32_t cdw0 __unused, void *buf, size_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 = ONE_BASED((cdw0 >> NVME_FEAT_LBA_RANGE_TYPE_NUM_SHIFT) &
+ NVME_FEAT_LBA_RANGE_TYPE_NUM_MASK);
+
+ if (num > len / sizeof(struct nvme_lba_range_type))
+ num = len / sizeof(struct nvme_lba_range_type);
+
+ printf("IDX\tTYPE\tSLBA\tNLB\tGUID\n");
+ for (i = 0; i < num; i++) {
+ if (idx > lba_range_type_name_res_max)
+ idx = 0;
+ printf("0x%zx %s", i, lba_range_type_name_res[idx]);
+ printf("\t0x%lx\t0x%lx\t", lba->slba, 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)
+{
+
+ printf(" Temperature Threshold (TMPTH): ");
+ print_temp((cdw0 >> NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_SHIFT) &
+ NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_MASK);
+}
+
+static void
+print_feature_error_recovery(uint32_t cdw0)
+{
+
+ printf(" Deallocated or Unwritten Logical Block Error Enable (DULBE): %d\n",
+ (cdw0 >> NVME_FEAT_ERROR_RECOVERY_DULBE_SHIFT) &
+ NVME_FEAT_ERROR_RECOVERY_DULBE_MASK);
+ printf(" Time Limited Error Recovery (TLER): %d of 100ms\n",
+ (cdw0 >> NVME_FEAT_ERROR_RECOVERY_TLER_SHIFT) &
+ NVME_FEAT_ERROR_RECOVERY_TLER_MASK);
+}
+
+static void
+print_feature_volatile_write_cache(uint32_t cdw0)
+{
+
+ printf(" Volatile Write Cache Enable (WCE): %d\n",
+ (cdw0 >> NVME_FEAT_VOLATILE_WRITE_CACHE_WCE_SHIFT) &
+ NVME_FEAT_VOLATILE_WRITE_CACHE_WCE_MASK);
+}
+
+static void
+print_feature_number_of_queues(uint32_t cdw0)
+{
+
+ printf(" Number of I/O Completion Queues Allocated (NCQA): %d\n",
+ ONE_BASED((cdw0 >> NVME_FEAT_NUMBER_OF_QUEUES_NCQA_SHIFT) &
+ NVME_FEAT_NUMBER_OF_QUEUES_NCQA_MASK));
+ printf(" Number of I/O Submission Queues Allocated (NSQA): %d\n",
+ ONE_BASED((cdw0 >> NVME_FEAT_NUMBER_OF_QUEUES_NSQA_SHIFT) &
+ NVME_FEAT_NUMBER_OF_QUEUES_NSQA_MASK));
+}
+
+static void
+print_feature_interrupt_coalescing(uint32_t cdw0)
+{
+
+ printf(" Aggregation Time (TIME): %d\n",
+ (cdw0 >> NVME_FEAT_INTERRUPT_COALESCING_TIME_SHIFT) &
+ NVME_FEAT_INTERRUPT_COALESCING_TIME_MASK);
+ printf(" Aggregation Threshold (THR): %d\n",
+ (cdw0 >> NVME_FEAT_INTERRUPT_COALESCING_THR_SHIFT) &
+ NVME_FEAT_INTERRUPT_COALESCING_THR_MASK);
+}
+
+static void
+print_feature_interrupt_vector_configuration(uint32_t cdw0)
+{
+
+ printf(" Coalescing Disable (CD): %d\n",
+ (cdw0 >> NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_CD_SHIFT) &
+ NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_CD_MASK);
+ printf(" Interrupt Vector (IV): %d\n",
+ (cdw0 >> NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_IV_SHIFT) &
+ NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_IV_MASK);
+}
+
+static void
+print_feature_write_atomicity(uint32_t cdw0)
+{
+
+ printf(" Disable Normal (DN): %d\n",
+ (cdw0 >> NVME_FEAT_WRITE_ATOMICITY_DN_SHIFT) &
+ NVME_FEAT_WRITE_ATOMICITY_DN_MASK);
+}
+
+static void
+print_feature_async_event_configuration(uint32_t cdw0)
+{
+
+ printf(" Telemetry Log Notices (TLN): %d\n",
+ (cdw0 >> NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN_SHIFT) &
+ NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN_MASK);
+ printf(" Firmware Activation Notices (FAN): %d\n",
+ (cdw0 >> NVME_FEAT_ASYNC_EVENT_CONFIGURATION_FAN_SHIFT) &
+ NVME_FEAT_ASYNC_EVENT_CONFIGURATION_FAN_MASK);
+ printf(" Namespace Attribute Notices (NAN): %d\n",
+ (cdw0 >> NVME_FEAT_ASYNC_EVENT_CONFIGURATION_NAN_SHIFT) &
+ NVME_FEAT_ASYNC_EVENT_CONFIGURATION_NAN_MASK);
+ printf(" SMART / Health Critical Warnings (HCW): 0x%x\n",
+ (cdw0 >> NVME_FEAT_ASYNC_EVENT_CONFIGURATION_HCW_SHIFT) &
+ NVME_FEAT_ASYNC_EVENT_CONFIGURATION_HCW_MASK);
+}
+
+static void
+print_feature_autonomous_power_state_transition(uint32_t cdw0)
+{
+ printf(" Autonomous Power State Transition Enable (APSTE): %d\n",
+ (cdw0 >> NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION_APSTE_SHIFT) &
+ NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION_APSTE_MASK);
+}
+
+static void
+print_feature_timestamp(uint32_t cdw0)
+{
+
+ printf(" Timestamp: %d\n",
+ (cdw0 >> NVME_FEAT_TIMESTAMP_TS_SHIFT) &
+ NVME_FEAT_TIMESTAMP_TS_MASK);
+}
+
+static void
+print_feature_keep_alive_timer(uint32_t cdw0)
+{
+
+ printf(" Keep Alive Timeout (KATO): %d\n",
+ (cdw0 >> NVME_FEAT_KEEP_ALIVE_TIMER_KATO_SHIFT) &
+ NVME_FEAT_KEEP_ALIVE_TIMER_KATO_MASK);
+}
+
+static void
+print_feature_host_memory_buffer(uint32_t cdw0)
+{
+
+ printf(" Memory Return (MR): %d\n",
+ (cdw0 >> NVME_FEAT_HOST_MEMORY_BUFFER_MR_SHIFT) &
+ NVME_FEAT_HOST_MEMORY_BUFFER_MR_MASK);
+ printf(" Enable Host Memory (EHM): %d\n",
+ (cdw0 >> NVME_FEAT_HOST_MEMORY_BUFFER_EHM_SHIFT) &
+ NVME_FEAT_HOST_MEMORY_BUFFER_EHM_MASK);
+}
+
+static void
+print_feature_host_controlled_thermal_mgmt(uint32_t cdw0)
+{
+
+ printf(" Thermal Management Temperature 1 (TMT1): %d\n",
+ (cdw0 >> NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT1_SHIFT) &
+ NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT1_MASK);
+ printf(" Thermal Management Temperature 2 (TMT2): %d\n",
+ (cdw0 >> NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT2_SHIFT) &
+ NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT2_MASK);
+}
+static void
+print_feature_non_op_power_state_config(uint32_t cdw0)
+{
+
+ printf(" Non-Operational Power State Permissive Mode Enable (NOPPME): %d\n",
+ (cdw0 >> NVME_FEAT_NON_OP_POWER_STATE_CONFIG_NOPPME_SHIFT) &
+ NVME_FEAT_NON_OP_POWER_STATE_CONFIG_NOPPME_MASK);
+}
+
+static void
+output_human(const uint8_t fid, uint32_t cdw0, uint8_t *buf, size_t len)
+{
+
+ switch (fid) {
+ case NVME_FEAT_ARBITRATION:
+ print_feature_arbitration(cdw0);
+ break;
+ case NVME_FEAT_POWER_MANAGEMENT:
+ print_feature_power_management(cdw0);
+ break;
+ case NVME_FEAT_LBA_RANGE_TYPE:
+ print_feature_lba_range_type(cdw0, buf, len);
+ break;
+ case NVME_FEAT_TEMPERATURE_THRESHOLD:
+ print_feature_temperature_threshold(cdw0);
+ break;
+ case NVME_FEAT_ERROR_RECOVERY:
+ print_feature_error_recovery(cdw0);
+ break;
+ case NVME_FEAT_VOLATILE_WRITE_CACHE:
+ print_feature_volatile_write_cache(cdw0);
+ break;
+ case NVME_FEAT_NUMBER_OF_QUEUES:
+ print_feature_number_of_queues(cdw0);
+ break;
+ case NVME_FEAT_INTERRUPT_COALESCING:
+ print_feature_interrupt_coalescing(cdw0);
+ break;
+ case NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION:
+ print_feature_interrupt_vector_configuration(cdw0);
+ break;
+ case NVME_FEAT_WRITE_ATOMICITY:
+ print_feature_write_atomicity(cdw0);
+ break;
+ case NVME_FEAT_ASYNC_EVENT_CONFIGURATION:
+ print_feature_async_event_configuration(cdw0);
+ break;
+ case NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION:
+ print_feature_autonomous_power_state_transition(cdw0);
+ break;
+ case NVME_FEAT_HOST_MEMORY_BUFFER:
+ print_feature_host_memory_buffer(cdw0);
+ break;
+ case NVME_FEAT_TIMESTAMP:
+ print_feature_timestamp(cdw0);
+ break;
+ case NVME_FEAT_KEEP_ALIVE_TIMER:
+ print_feature_keep_alive_timer(cdw0);
+ break;
+ case NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT:
+ print_feature_host_controlled_thermal_mgmt(cdw0);
+ break;
+ case NVME_FEAT_NON_OP_POWER_STATE_CONFIG:
+ print_feature_non_op_power_state_config(cdw0);
+ break;
+ default:
+ printf(" cdw0 = 0x%#08x\n", cdw0);
+ break;
+ }
+}
+
+static void
+output_binary(const uint8_t fid __unused, uint32_t cdw0, void *buf __unused, size_t len __unused)
+{
+ uint32_t i;
+
+ printf("cdw0 = ");
+ for(i = 0; i < sizeof(cdw0) * 2; i++) {
+ printf("%s", bits[(cdw0 >> i*4) & 0x0F]);
+ printf(" ");
+ }
+ printf("\n");
+}
+
+static void
+feature_get(const struct cmd *f, int argc, char *argv[])
+{
+ struct nvme_pt_command pt;
+ uint8_t fid, sel;
+ uint32_t cdw0, cdw11;
+ int fd;
+ uint8_t buf[4096];
+
+ if (arg_parse(argc, argv, f))
+ return;
+
+ if (get_opt.fid == NONE8) {
+ fprintf(stderr,
+ "feature id not specified\n");
+ arg_help(argc, argv, f);
+ }
+ if (get_opt.sel > 0x3) {
+ fprintf(stderr,
+ "select value over range\n");
+ arg_help(argc, argv, f);
+ }
+
+ open_dev(get_opt.dev, &fd, 1, 1);
+
+ fid = get_opt.fid;
+ sel = get_opt.sel;
+ cdw11 = get_opt.cdw11;
+
+ if (sel >= sel_name_res_max)
+ errx(EX_IOERR, "invalid select value");
+
+ 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);
+ if (fid == NVME_FEAT_LBA_RANGE_TYPE){
+ memset(buf, 0, sizeof(buf));
+ pt.buf = buf;
+ pt.len = sizeof(buf);
+ } else {
+ pt.buf = NULL;
+ pt.len = 0;
+ }
+ pt.is_read = 1;
+
+ if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
+ err(EX_IOERR, "passthrough request failed");
+
+ printf("%s(0x%x), %s value\n",
+ (fid < feature_name_res_max ? feature_name_res[fid] : "\0"), fid,
+ sel_name_res[sel]);
+
+ if (nvme_completion_is_error(&pt.cpl)) {
+ if (NVME_STATUS_GET_SCT((&pt.cpl)->status) == 0)
+ errx(EX_IOERR, "%s", nvme_gcs_to_str(NVME_STATUS_GET_SC((&pt.cpl)->status)));
+ else if (NVME_STATUS_GET_SCT((&pt.cpl)->status) == 1)
+ errx(EX_IOERR, "%s", nvme_cse_to_str(NVME_STATUS_GET_SC((&pt.cpl)->status)));
+ else
+ errx(EX_IOERR, "get feature returned error");
+ }
+
+ cdw0 = pt.cpl.cdw0;
+
+ if ( get_opt.binary )
+ output_binary(fid, cdw0, buf, sizeof(buf));
+ else
+ output_human(fid, cdw0, buf, sizeof(buf));
+
+ close(fd);
+ exit(0);
+}
+
+static void
+feature_set(const struct cmd *f, int argc, char *argv[])
+{
+ struct nvme_pt_command pt;
+ uint8_t fid;
+ bool save;
+ uint32_t cdw11, cdw12, cdw13, cdw14, cdw15;
+ int fd;
+
+ if (arg_parse(argc, argv, f))
+ return;
+
+ if (set_opt.fid == NONE8) {
+ fprintf(stderr,
+ "feature id not specified\n");
+ arg_help(argc, argv, f);
+ }
+
+ fid = set_opt.fid;
+ save = set_opt.save;
+ cdw11 = set_opt.cdw11;
+ cdw12 = set_opt.cdw12;
+ cdw13 = set_opt.cdw13;
+ cdw14 = set_opt.cdw14;
+ cdw15 = set_opt.cdw15;
+
+ open_dev(set_opt.dev, &fd, 1, 1);
+
+ memset(&pt, 0, sizeof(pt));
+ pt.cmd.opc = NVME_OPC_SET_FEATURES;
+ pt.cmd.cdw10 = htole32((save << NVME_FEAT_SET_SV_SHIFT) |
+ (fid << NVME_FEAT_SET_FID_SHIFT));
+ pt.cmd.cdw11 = htole32(cdw11);
+ pt.cmd.cdw12 = htole32(cdw12);
+ pt.cmd.cdw13 = htole32(cdw13);
+ pt.cmd.cdw14 = htole32(cdw14);
+ pt.cmd.cdw15 = htole32(cdw15);
+ pt.buf = NULL;
+ pt.len = 0;
+ 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) == 0)
+ errx(EX_IOERR, "%s", nvme_gcs_to_str(NVME_STATUS_GET_SC((&pt.cpl)->status)));
+ else if (NVME_STATUS_GET_SCT((&pt.cpl)->status) == 1)
+ errx(EX_IOERR, "%s", nvme_cse_to_str(NVME_STATUS_GET_SC((&pt.cpl)->status)));
+ else
+ errx(EX_IOERR, "set feature returned error");
+ }
+
+ 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;
+ uint8_t fid, sel;
+ uint32_t cdw0, cdw11;
+ int fd;
+ uint32_t i;
+
+ 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;
+
+ if (sel >= sel_name_res_max)
+ continue;
+
+ 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 = NULL;
+ pt.len = 0;
+ pt.is_read = 1;
+
+ if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
+ continue;
+
+ printf("%s(0x%x), %s value\n",
+ (fid < feature_name_res_max ? feature_name_res[fid] : "\0"), fid,
+ sel_name_res[sel]);
+
+ if (nvme_completion_is_error(&pt.cpl))
+ continue;
+
+ cdw0 = pt.cpl.cdw0;
+ output_human(fid, cdw0, NULL, 0);
+ }
+
+ close(fd);
+ exit(0);
+}
+
+static void
+feature(const struct cmd *nf __unused, int argc, char *argv[])
+{
+
+ cmd_dispatch(argc, argv, &feature_cmd);
+}
Index: sbin/nvmecontrol/nvmecontrol.h
===================================================================
--- sbin/nvmecontrol/nvmecontrol.h
+++ sbin/nvmecontrol/nvmecontrol.h
@@ -64,6 +64,9 @@
const char *kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key);
+const char *nvme_gcs_to_str(uint8_t sc);
+const char *nvme_cse_to_str(uint8_t sc);
+
void logpage_register(struct logpage_function *p);
#define NVME_CTRLR_PREFIX "nvme"
#define NVME_NS_PREFIX "ns"
Index: sbin/nvmecontrol/nvmecontrol.c
===================================================================
--- sbin/nvmecontrol/nvmecontrol.c
+++ sbin/nvmecontrol/nvmecontrol.c
@@ -176,6 +176,224 @@
*nsid = gnsid.nsid;
}
+const char *
+nvme_gcs_to_str(uint8_t sc)
+{
+ const char *str;
+
+ switch (sc) {
+ case NVME_SC_SUCCESS:
+ str = "Successful Completion";
+ break;
+ case NVME_SC_INVALID_OPCODE:
+ str = "Invalid Command Opcode";
+ break;
+ case NVME_SC_INVALID_FIELD:
+ str = "Invalid Field in Command";
+ break;
+ case NVME_SC_COMMAND_ID_CONFLICT:
+ str = "Command ID Conflict";
+ break;
+ case NVME_SC_DATA_TRANSFER_ERROR:
+ str = "Data Transfer Error";
+ break;
+ case NVME_SC_ABORTED_POWER_LOSS:
+ str = "Commands Aborted due to Power Loss Notification";
+ break;
+ case NVME_SC_INTERNAL_DEVICE_ERROR:
+ str = "Internal Error";
+ break;
+ case NVME_SC_ABORTED_BY_REQUEST:
+ str = "Command Abort Requested";
+ break;
+ case NVME_SC_ABORTED_SQ_DELETION:
+ str = "Command Aborted due to SQ Deletion";
+ break;
+ case NVME_SC_ABORTED_FAILED_FUSED:
+ str = "Command Aborted due to Failed Fused Command";
+ break;
+ case NVME_SC_ABORTED_MISSING_FUSED:
+ str = "Command Aborted due to Missing Fused Command";
+ break;
+ case NVME_SC_INVALID_NAMESPACE_OR_FORMAT:
+ str = "Invalid Namespace or Format";
+ break;
+ case NVME_SC_COMMAND_SEQUENCE_ERROR:
+ str = "Command Sequence Error";
+ break;
+ case NVME_SC_INVALID_SGL_SEGMENT_DESCR:
+ str = "Invalid SGL Segment Descriptor";
+ break;
+ case NVME_SC_INVALID_NUMBER_OF_SGL_DESCR:
+ str = "Invalid Number of SGL Descriptors";
+ break;
+ case NVME_SC_DATA_SGL_LENGTH_INVALID:
+ str = "Data SGL Length Invalid";
+ break;
+ case NVME_SC_METADATA_SGL_LENGTH_INVALID:
+ str = "Metadata SGL Length Invalid";
+ break;
+ case NVME_SC_SGL_DESCRIPTOR_TYPE_INVALID:
+ str = "SGL Descriptor Type Invalid";
+ break;
+ case NVME_SC_INVALID_USE_OF_CMB:
+ str = "Invalid Use of Controller Memory Buffer";
+ break;
+ case NVME_SC_PRP_OFFET_INVALID:
+ str = "PRP Offset Invalid";
+ break;
+ case NVME_SC_ATOMIC_WRITE_UNIT_EXCEEDED:
+ str = "Atomic Write Unit Exceeded";
+ break;
+ case NVME_SC_OPERATION_DENIED:
+ str = "Operation Denied";
+ break;
+ case NVME_SC_SGL_OFFSET_INVALID:
+ str = "SGL Offset Invalid";
+ break;
+ case NVME_SC_HOST_ID_INCONSISTENT_FORMAT:
+ str = "Host Identifier Inconsistent Format";
+ break;
+ case NVME_SC_KEEP_ALIVE_TIMEOUT_EXPIRED:
+ str = "Keep Alive Timeout Expired";
+ break;
+ case NVME_SC_KEEP_ALIVE_TIMEOUT_INVALID:
+ str = "Keep Alive Timeout Invalid";
+ break;
+ case NVME_SC_ABORTED_DUE_TO_PREEMPT:
+ str = "Command Aborted due to Preempt and Abort";
+ break;
+ case NVME_SC_SANITIZE_FAILED:
+ str = "Sanitize Failed";
+ break;
+ case NVME_SC_SANITIZE_IN_PROGRESS:
+ str = "Sanitize In Progress";
+ break;
+ case NVME_SC_SGL_DATA_BLOCK_GRAN_INVALID:
+ str = "SGL Data Block Granularity Invalid";
+ break;
+ case NVME_SC_NOT_SUPPORTED_IN_CMB:
+ str = "Command Not Supported for Queue in CMB";
+ break;
+ default:
+ str = "Unknown Error";
+ }
+
+ return str;
+}
+
+const char *
+nvme_cse_to_str(uint8_t sct)
+{
+ const char *str;
+
+ switch (sct) {
+ case NVME_SC_COMPLETION_QUEUE_INVALID:
+ str = "Completion Queue Invalid";
+ break;
+ case NVME_SC_INVALID_QUEUE_IDENTIFIER:
+ str = "Invalid Queue Identifier";
+ break;
+ case NVME_SC_MAXIMUM_QUEUE_SIZE_EXCEEDED:
+ str = "Invalid Queue Size";
+ break;
+ case NVME_SC_ABORT_COMMAND_LIMIT_EXCEEDED:
+ str = "Abort Command Limit Exceeded";
+ break;
+ case NVME_SC_ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED:
+ str = "Asynchronous Event Request Limit Exceeded";
+ break;
+ case NVME_SC_INVALID_FIRMWARE_SLOT:
+ str = "Invalid Firmware Slot";
+ break;
+ case NVME_SC_INVALID_FIRMWARE_IMAGE:
+ str = "Invalid Firmware Image";
+ break;
+ case NVME_SC_INVALID_INTERRUPT_VECTOR:
+ str = "Invalid Interrupt Vector";
+ break;
+ case NVME_SC_INVALID_LOG_PAGE:
+ str = "Invalid Log Page";
+ break;
+ case NVME_SC_INVALID_FORMAT:
+ str = "Invalid Format";
+ break;
+ case NVME_SC_FIRMWARE_REQUIRES_RESET:
+ str = "Firmware Activation Requires Conventional Reset";
+ break;
+ case NVME_SC_INVALID_QUEUE_DELETION:
+ str = "Invalid Queue Deletion";
+ break;
+ case NVME_SC_FEATURE_NOT_SAVEABLE:
+ str = "Feature Identifier Not Saveable";
+ break;
+ case NVME_SC_FEATURE_NOT_CHANGEABLE:
+ str = "Feature Not Changeable";
+ break;
+ case NVME_SC_FEATURE_NOT_NS_SPECIFIC:
+ str = "Feature Not Namespace Specific";
+ break;
+ case NVME_SC_FW_ACT_REQUIRES_NVMS_RESET:
+ str = "Firmware Activation Requires NVM Subsystem Reset";
+ break;
+ case NVME_SC_FW_ACT_REQUIRES_RESET:
+ str = "Firmware Activation Requires Reset";
+ break;
+ case NVME_SC_FW_ACT_REQUIRES_TIME:
+ str = "Firmware Activation Requires Maximum Time Violation";
+ break;
+ case NVME_SC_FW_ACT_PROHIBITED:
+ str = "Firmware Activation Prohibited";
+ break;
+ case NVME_SC_OVERLAPPING_RANGE:
+ str = "Overlapping Range";
+ break;
+ case NVME_SC_NS_INSUFFICIENT_CAPACITY:
+ str = "Namespace Insufficient Capacity";
+ break;
+ case NVME_SC_NS_ID_UNAVAILABLE:
+ str = "Namespace Identifier Unavailable";
+ break;
+ case NVME_SC_NS_ALREADY_ATTACHED:
+ str = "Namespace Already Attached";
+ break;
+ case NVME_SC_NS_IS_PRIVATE:
+ str = "Namespace Is Private";
+ break;
+ case NVME_SC_NS_NOT_ATTACHED:
+ str = "Namespace Not Attached";
+ break;
+ case NVME_SC_THIN_PROV_NOT_SUPPORTED:
+ str = "Thin Provisioning Not Supported";
+ break;
+ case NVME_SC_CTRLR_LIST_INVALID:
+ str = "Controller List Invalid";
+ break;
+ case NVME_SC_SELF_TEST_IN_PROGRESS:
+ str = "Device Self-test In Progress";
+ break;
+ case NVME_SC_BOOT_PART_WRITE_PROHIB:
+ str = "Boot Partition Write Prohibited";
+ break;
+ case NVME_SC_INVALID_CTRLR_ID:
+ str = "Invalid Controller Identifier";
+ break;
+ case NVME_SC_INVALID_SEC_CTRLR_STATE:
+ str = "Invalid Secondary Controller State";
+ break;
+ case NVME_SC_INVALID_NUM_OF_CTRLR_RESRC:
+ str = "Invalid Number of Controller Resources";
+ break;
+ case NVME_SC_INVALID_RESOURCE_ID:
+ str = "Invalid Resource Identifier";
+ break;
+ default:
+ str = "Unknown Error";
+ }
+
+ return str;
+}
+
int
main(int argc, char *argv[])
{
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Oct 22, 10:00 PM (8 h, 25 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
24069709
Default Alt Text
D32700.id97992.diff (31 KB)
Attached To
Mode
D32700: nvmecontrol: Implement Get/Set Feature for nvmecontrol
Attached
Detach File
Event Timeline
Log In to Comment