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 <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 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) | |||||
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", | |||||
[NVME_FEAT_ARBITRATION] = "Command Arbitration", | |||||
[NVME_FEAT_POWER_MANAGEMENT] = "Power Management", | |||||
[NVME_FEAT_LBA_RANGE_TYPE] = "LBA Range Type", | |||||
[NVME_FEAT_TEMPERATURE_THRESHOLD] = "Temperature Threshold", | |||||
[NVME_FEAT_ERROR_RECOVERY] = "Error Recovery", | |||||
[NVME_FEAT_VOLATILE_WRITE_CACHE] = "Volatile Write Cache", | |||||
[NVME_FEAT_NUMBER_OF_QUEUES] = "Number of Queues", | |||||
[NVME_FEAT_INTERRUPT_COALESCING] = "Interrupt Coalescing", | |||||
[NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION] = "Interrupt Vector Configuration", | |||||
[NVME_FEAT_WRITE_ATOMICITY] = "Write Atomicity Normal", | |||||
[NVME_FEAT_ASYNC_EVENT_CONFIGURATION] = "Asynchronous Event Configuration", | |||||
[NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION] = "Autonomous Power State Transition", | |||||
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. | |||||
[NVME_FEAT_HOST_MEMORY_BUFFER] = "Host Memory Buffer", | |||||
[NVME_FEAT_TIMESTAMP] = "Timestamp", | |||||
[NVME_FEAT_KEEP_ALIVE_TIMER] = "Keep Alive Timer", | |||||
[NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT] = "Host Controlled Thermal Management", | |||||
[NVME_FEAT_NON_OP_POWER_STATE_CONFIG] = "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] = "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 { | |||||
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"), | |||||
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? | |||||
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 *datafile; | |||||
const char *dev; | |||||
} set_opt = { | |||||
.fid = NONE8, | |||||
.save = false, | |||||
.cdw11 = 0, | |||||
.cdw12 = 0, | |||||
.cdw13 = 0, | |||||
.cdw14 = 0, | |||||
.cdw15 = 0, | |||||
.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("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 void | |||||
print_feature_arbitration(uint32_t cdw0) | |||||
{ | |||||
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) | |||||
{ | |||||
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, 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(NVME_FEAT_LBA_RANGE_TYPE_NUM(cdw0)); | |||||
if (num > len / sizeof(struct nvme_lba_range_type)) | |||||
num = len / sizeof(struct nvme_lba_range_type); | |||||
for (i = 0; i < num; i++) { | |||||
if (1 == 0 ) | |||||
printf("IDX\tTYPE\tSLBA\tNLB\tGUID\n"); | |||||
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(NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH(cdw0)); | |||||
} | |||||
static void | |||||
print_feature_error_recovery(uint32_t cdw0) | |||||
{ | |||||
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) | |||||
{ | |||||
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) | |||||
{ | |||||
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) | |||||
{ | |||||
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) | |||||
{ | |||||
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) | |||||
{ | |||||
printf(" Disable Normal (DN): %d\n", | |||||
NVME_FEAT_WRITE_ATOMICITY_DN(cdw0)); | |||||
} | |||||
static void | |||||
print_feature_async_event_configuration(uint32_t cdw0) | |||||
{ | |||||
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: 0x%x\n", | |||||
NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN(cdw0)); | |||||
} | |||||
static void | |||||
print_feature_autonomous_power_state_transition(uint32_t cdw0) | |||||
{ | |||||
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) | |||||
{ | |||||
printf(" Timestamp: %d\n", | |||||
NVME_FEAT_TIMESTAMP_TS(cdw0)); | |||||
} | |||||
static void | |||||
print_feature_keep_alive_timer(uint32_t cdw0) | |||||
{ | |||||
printf(" Keep Alive Timeout (KATO): %d\n", | |||||
NVME_FEAT_KEEP_ALIVE_TIMER_KATO(cdw0)); | |||||
} | |||||
static void | |||||
print_feature_host_memory_buffer(uint32_t cdw0) | |||||
{ | |||||
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 | |||||
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. | |||||
print_feature_host_controlled_thermal_mgmt(uint32_t cdw0) | |||||
{ | |||||
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) | |||||
{ | |||||
printf(" Non-Operational Power State Permissive Mode Enable (NOPPME): %d\n", | |||||
NVME_FEAT_NON_OP_POWER_STATE_CONFIG_NOPPME(cdw0)); | |||||
} | |||||
static void | |||||
output_human(const uint8_t fid, uint32_t cdw0, void *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 | |||||
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_get(const struct cmd *f, int argc, char *argv[]) | |||||
{ | |||||
struct nvme_pt_command pt; | |||||
int fd; | |||||
uint8_t *buf = NULL; | |||||
uint32_t size = 0; | |||||
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); | |||||
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… | |||||
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… | |||||
if (get_opt.sel >= sel_name_res_max) | |||||
errx(EX_IOERR, "invalid select value"); | |||||
if (get_opt.fid == NVME_FEAT_LBA_RANGE_TYPE){ | |||||
size = 4096; | |||||
if ((buf = malloc(size)) == NULL) | |||||
errx(EX_OSERR, "unable to malloc %d bytes", size); | |||||
memset(buf, 0, size); | |||||
} | |||||
memset(&pt, 0, sizeof(pt)); | |||||
pt.cmd.opc = NVME_OPC_GET_FEATURES; | |||||
pt.cmd.cdw10 = htole32(NVME_FEAT_GET_SEL(get_opt.sel)); | |||||
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"); | |||||
printf("%s(0x%x), %s value\n", | |||||
(get_opt.fid < feature_name_res_max ? feature_name_res[get_opt.fid] : "\0"), get_opt.fid, | |||||
sel_name_res[get_opt.sel]); | |||||
if (nvme_completion_is_error(&pt.cpl)) { | |||||
errx(EX_IOERR, "%s", | |||||
get_status_string(NVME_STATUS_GET_SCT(pt.cpl.status), | |||||
NVME_STATUS_GET_SC(pt.cpl.status))); | |||||
} | |||||
if ( get_opt.binary ) | |||||
output_binary(get_opt.fid, pt.cpl.cdw0, buf, sizeof(buf)); | |||||
else | |||||
output_human(get_opt.fid, pt.cpl.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; | |||||
int fd; | |||||
void *buf = NULL; | |||||
int32_t size = 0; | |||||
if (arg_parse(argc, argv, f)) | |||||
return; | |||||
if (set_opt.fid == NONE8) { | |||||
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); | |||||
} | |||||
open_dev(set_opt.dev, &fd, 1, 1); | |||||
memset(&pt, 0, sizeof(pt)); | |||||
pt.cmd.opc = NVME_OPC_SET_FEATURES; | |||||
pt.cmd.cdw10 = htole32(NVME_FEAT_SET_SV(set_opt.fid)); | |||||
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)) { | |||||
errx(EX_IOERR, "%s", | |||||
get_status_string(NVME_STATUS_GET_SCT(pt.cpl.status), | |||||
NVME_STATUS_GET_SC(pt.cpl.status))); | |||||
} | |||||
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(NVME_FEAT_GET_SEL(fid)); | |||||
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?