Changeset View
Standalone View
sbin/nvmecontrol/feature.c
- This file was added.
/*- | |||||
* 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) | |||||
wanpengqian_gmail.com: Better moving these defintions to `sys/dev/nvme/nvme.h`? | |||||
Done Inline ActionsI think all of these would be better in sys/dev/nvme/nvme.h. Can you move them there? imp: I think all of these would be better in sys/dev/nvme/nvme.h. Can you move them there? | |||||
Done Inline ActionsI will create a new Differential for these. wanpengqian_gmail.com: I will create a new Differential for these. | |||||
#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", | |||||
impUnsubmitted Done Inline ActionsAre there #defines that can be used instead of numbers here? imp: Are there #defines that can be used instead of numbers here? | |||||
[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) | |||||
{ | |||||
Done Inline Actions"milliseconds" rpokala: "milliseconds" | |||||
Done Inline ActionsThanks. I always spell millisecond to milisecond. It's my fault. wanpengqian_gmail.com: Thanks. I always spell millisecond to milisecond. It's my fault. | |||||
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); | |||||
impUnsubmitted Done Inline ActionsIdeally, you'd handle this the same way that we handle the different log pages. There's a structure there that looks up the page type and then uses a routine that matches that type. There's also an optional vendor field for that. If you were to have something like that, rather than this huge switch table, you could more easily extend it with loadable modules. That's where I was going with my other comments. I'm happy to provide additional details if you think that's helpful... imp: Ideally, you'd handle this the same way that we handle the different log pages. There's a… | |||||
wanpengqian_gmail.comAuthorUnsubmitted Done Inline ActionsHandle features just like logpage. and implement intel p3700's vendor features. It seems to be working. wanpengqian_gmail.com: Handle features just like logpage. and implement intel p3700's vendor features. It seems to be… | |||||
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); | |||||
} |
Better moving these defintions to sys/dev/nvme/nvme.h?