summaryrefslogtreecommitdiffstats
path: root/nvme.c
diff options
context:
space:
mode:
Diffstat (limited to 'nvme.c')
-rw-r--r--nvme.c512
1 files changed, 501 insertions, 11 deletions
diff --git a/nvme.c b/nvme.c
index c10932f..862f9b6 100644
--- a/nvme.c
+++ b/nvme.c
@@ -365,18 +365,25 @@ ret:
return nvme_status_to_errno(err, false);
}
+__u16 get_feat_buf_len(unsigned short feature) {
+ return nvme_feat_buf_len[feature];
+}
+
static int get_telemetry_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Retrieve telemetry log and write to binary file";
const char *fname = "File name to save raw binary, includes header";
const char *hgen = "Have the host tell the controller to generate the report";
const char *cgen = "Gather report generated by the controller.";
- const char *dgen = "Pick which telemetry data area to report. Default is all. Valid options are 1, 2, 3.";
+ const char *dgen = "Pick which telemetry data area to report. Default is 3 to fetch areas 1-3. Valid options are 1, 2, 3, 4.";
const size_t bs = 512;
struct nvme_telemetry_log_page_hdr *hdr;
+ struct nvme_id_ctrl ctrl;
size_t full_size, offset = bs;
int err = -1, fd, output;
void *page_log;
+ __u32 result;
+ void *buf = NULL;
struct config {
char *file_name;
@@ -454,6 +461,46 @@ static int get_telemetry_log(int argc, char **argv, struct command *cmd, struct
case 3:
full_size = (le16_to_cpu(hdr->dalb3) * bs) + offset;
break;
+ case 4:
+ err = nvme_identify_ctrl(fd, &ctrl);
+ if (err) {
+ perror("identify-ctrl");
+ goto close_output;
+ }
+
+ if (posix_memalign(&buf, getpagesize(), nvme_feat_buf_len[NVME_FEAT_HOST_BEHAVIOR])) {
+ fprintf(stderr, "can not allocate feature payload\n");
+ errno = ENOMEM;
+ err = -1;
+ goto close_output;
+ }
+ memset(buf, 0, nvme_feat_buf_len[NVME_FEAT_HOST_BEHAVIOR]);
+
+ err = nvme_get_feature(fd, NVME_NSID_ALL, NVME_FEAT_HOST_BEHAVIOR, 0, 0,
+ 0, nvme_feat_buf_len[NVME_FEAT_HOST_BEHAVIOR], buf, &result);
+ if (err > 0) {
+ nvme_show_status(err);
+ } else if (err < 0) {
+ perror("get-feature");
+ } else {
+ if ((ctrl.lpa & 0x40)) {
+ if (((unsigned char *)buf)[1] == 1)
+ full_size = (le32_to_cpu(hdr->dalb4) * bs) + offset;
+ else {
+ fprintf(stderr, "Data area 4 unsupported, Host Behavior Support ETDAS not set to 1\n");
+ errno = EINVAL;
+ err = -1;
+ }
+ } else {
+ fprintf(stderr, "Data area 4 unsupported, bit 6 of Log Page Attributes not set\n");
+ errno = EINVAL;
+ err = -1;
+ }
+ }
+ free(buf);
+ if (err)
+ goto close_output;
+ break;
default:
fprintf(stderr, "Invalid data area requested\n");
errno = EINVAL;
@@ -593,6 +640,56 @@ ret:
return nvme_status_to_errno(err, false);
}
+static int get_supported_log_pages(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve supported logs and print the table.";
+ const char *verbose = "Increase output verbosity";
+ struct nvme_support_log_pages supports;
+
+ int err = -1, fd;
+ enum nvme_print_flags flags;
+
+ struct config {
+ int verbose;
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
+ OPT_FLAG("verbose", 'v', &cfg.verbose, verbose),
+ OPT_END()
+ };
+
+ err = fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ goto ret;
+
+ err = flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto close_fd;
+
+ if (cfg.verbose)
+ flags |= VERBOSE;
+
+ err = nvme_supported_log(fd, &supports);
+ if (!err)
+ nvme_show_supported_log(&supports, devicename, flags);
+ else if (err > 0)
+ nvme_show_status(err);
+ else
+ perror("supported log pages");
+
+close_fd:
+ close(fd);
+ret:
+ return nvme_status_to_errno(err, false);
+}
+
static int get_error_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Retrieve specified number of "\
@@ -929,7 +1026,7 @@ static int get_persistent_event_log(int argc, char **argv,
const char *log_len = "number of bytes to retrieve";
const char *raw = "use binary output";
void *pevent_log_info;
- struct nvme_persistent_event_log_head *pevent_log_head = NULL;
+ struct nvme_persistent_event_log_head *pevent_log_head, *collected_head;
enum nvme_print_flags flags;
int err = -1, fd;
bool huge;
@@ -985,22 +1082,22 @@ static int get_persistent_event_log(int argc, char **argv,
sizeof(*pevent_log_head), pevent_log_head);
if (err < 0) {
perror("persistent event log");
- goto close_fd;
+ goto free_head;
} else if (err) {
nvme_show_status(err);
- goto close_fd;
+ goto free_head;
}
if (cfg.action == NVME_PEVENT_LOG_RELEASE_CTX) {
printf("Releasing Persistent Event Log Context\n");
- goto close_fd;
+ goto free_head;
}
if (!cfg.log_len && cfg.action != NVME_PEVENT_LOG_EST_CTX_AND_READ) {
cfg.log_len = le64_to_cpu(pevent_log_head->tll);
} else if (!cfg.log_len && cfg.action == NVME_PEVENT_LOG_EST_CTX_AND_READ) {
printf("Establishing Persistent Event Log Context\n");
- goto close_fd;
+ goto free_head;
}
/*
@@ -1018,22 +1115,39 @@ static int get_persistent_event_log(int argc, char **argv,
perror("could not alloc buffer for persistent event log page\n");
errno = ENOMEM;
err = -1;
- goto close_fd;
+ goto free_head;
}
err = nvme_persistent_event_log(fd, cfg.action,
cfg.log_len, pevent_log_info);
- if (!err)
+ if (!err) {
+ err = nvme_persistent_event_log(fd, cfg.action,
+ sizeof(*pevent_log_head), pevent_log_head);
+ if (err < 0) {
+ perror("persistent event log");
+ goto free;
+ } else if (err) {
+ nvme_show_status(err);
+ goto free;
+ }
+ collected_head = pevent_log_info;
+ if(collected_head->gen_number != pevent_log_head->gen_number) {
+ printf("Collected Persistent Event Log may be invalid, "\
+ "Re-read the log is reiquired\n");
+ goto free;
+ }
nvme_show_persistent_event_log(pevent_log_info, cfg.action,
cfg.log_len, devicename, flags);
+ }
else if (err > 0)
nvme_show_status(err);
else
perror("persistent event log");
+free:
nvme_free(pevent_log_info, huge);
-
-close_fd:
+free_head:
free(pevent_log_head);
+close_fd:
close(fd);
ret:
return nvme_status_to_errno(err, false);
@@ -1249,6 +1363,113 @@ ret:
}
+static int get_boot_part_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Retrieve Boot Partition " \
+ "log page and prints it, for the given " \
+ "device in either decoded format(default), " \
+ "json or binary.";
+ const char *lsp = "log specific field";
+ const char *fname = "boot partition data output file name";
+ struct nvme_boot_part_hdr boot;
+ __u8 *bp_log;
+ enum nvme_print_flags flags;
+ int err = -1, fd = 0, output = 0;
+ __u32 bpsz = 0;
+
+ struct config {
+ __u8 lsp;
+ char *output_format;
+ char *file_name;
+ };
+
+ struct config cfg = {
+ .lsp = 0,
+ .file_name = NULL,
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_BYTE("lsp", 's', &cfg.lsp, lsp),
+ OPT_FILE("output-file", 'f', &cfg.file_name, fname),
+ OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
+ OPT_END()
+ };
+
+ err = fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ goto ret;
+
+ err = flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto close_fd;
+
+ if (!cfg.file_name) {
+ fprintf(stderr, "Please provide an output file!\n");
+ errno = EINVAL;
+ err = -1;
+ goto close_fd;
+ }
+
+ if (cfg.lsp > 128) {
+ fprintf(stderr, "invalid lsp param: %u\n", cfg.lsp);
+ errno = EINVAL;
+ err = -1;
+ goto close_fd;
+ }
+
+ output = open(cfg.file_name, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (output < 0) {
+ fprintf(stderr, "Failed to open output file %s: %s!\n",
+ cfg.file_name, strerror(errno));
+ err = output;
+ goto close_fd;
+ }
+
+ err = nvme_boot_part_log(fd, cfg.lsp, &boot, sizeof(boot));
+ if (err < 0) {
+ perror("boot partition log");
+ goto close_output;
+ } else if (err) {
+ nvme_show_status(err);
+ goto close_output;
+ }
+
+ bpsz = (boot.bpinfo & 0x7fff) * 128 * 1024;
+ bp_log = calloc(sizeof(boot) + bpsz, 1);
+ if (!bp_log) {
+ perror("could not alloc buffer for boot partition log");
+ errno = ENOMEM;
+ err = -1;
+ goto close_output;
+ }
+
+ err = nvme_boot_part_log(fd, cfg.lsp, &bp_log, sizeof(boot) + bpsz);
+ if (!err)
+ nvme_show_boot_part_log(&bp_log, devicename, flags, sizeof(boot) + bpsz);
+ else if (err > 0)
+ nvme_show_status(err);
+ else
+ perror("boot partition log");
+
+ err = write(output, (void *) bp_log + sizeof(boot), bpsz);
+ if (err != bpsz) {
+ fprintf(stderr, "Failed to flush all data to file!\n");
+ } else {
+ printf("Data flushed into file %s\n", cfg.file_name);
+ }
+ err = 0;
+
+ free(bp_log);
+
+close_output:
+ close(output);
+close_fd:
+ close(fd);
+ret:
+ return nvme_status_to_errno(err, false);
+}
+
static int get_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Retrieve desired number of bytes "\
@@ -1428,6 +1649,53 @@ ret:
return nvme_status_to_errno(err, false);
}
+static int get_fid_support_effects_log(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve FID Support and Effects log and show it.";
+ const char *human_readable = "show log in readable format";
+ struct nvme_fid_support_effects fid_support_log;
+ enum nvme_print_flags flags;
+ int fd, err = -1;
+
+ struct config {
+ int human_readable;
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
+ OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable),
+ OPT_END()
+ };
+
+ err = fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ goto ret;
+
+ err = flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto close_fd;
+ if (cfg.human_readable)
+ flags |= VERBOSE;
+
+ err = nvme_fid_support_effects_log(fd, &fid_support_log);
+ if (!err)
+ nvme_show_fid_support_effects_log(&fid_support_log, devicename, flags);
+ else if (err > 0)
+ nvme_show_status(err);
+ else
+ perror("fid support effects log");
+close_fd:
+ close(fd);
+ret:
+ return nvme_status_to_errno(err, false);
+}
+
static int list_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Show controller list information for the subsystem the "\
@@ -1554,6 +1822,67 @@ ret:
return nvme_status_to_errno(err, false);
}
+static int id_endurance_grp_list(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ const char *desc = "Show endurance group list information for the given endurance "\
+ "group id";
+ const char *endurance_grp_id = "Endurance Group ID";
+ int err = -1, fd;
+ struct nvme_endurance_group_list *endgrp_list;
+ enum nvme_print_flags flags;
+
+ struct config {
+ __u16 endgrp_id;
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .endgrp_id = 0,
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_SHRT("endgrp-id", 'i', &cfg.endgrp_id, endurance_grp_id),
+ OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
+ OPT_END()
+ };
+
+ err = fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ goto ret;
+
+ err = flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto close_fd;
+ if (flags != JSON && flags != NORMAL) {
+ err = -EINVAL;
+ fprintf(stderr, "invalid output format\n");
+ goto close_fd;
+ }
+
+ if (posix_memalign((void *)&endgrp_list, getpagesize(), 0x1000)) {
+ fprintf(stderr, "can not allocate memory for endurance gropu list\n");
+ errno = ENOMEM;
+ err = -1;
+ goto close_fd;
+ }
+
+ err = nvme_identify_endurance_group_list(fd, cfg.endgrp_id, endgrp_list);
+ if (!err)
+ nvme_show_endurance_group_list(endgrp_list, flags);
+ else if (err > 0)
+ nvme_show_status(err);
+ else
+ perror("id endurance group list");
+
+ free(endgrp_list);
+close_fd:
+ close(fd);
+ret:
+ return nvme_status_to_errno(err, false);
+}
+
static int delete_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Delete the given namespace by "\
@@ -2224,6 +2553,73 @@ ret:
return nvme_status_to_errno(err, false);
}
+static int cmd_set_independent_id_ns(int argc, char **argv,
+ struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Send an I/O Command Set Independent Identify "\
+ "Namespace command to the given device, returns properties of the "\
+ "specified namespace in human-readable or binary or json format.";
+ const char *raw = "show identify in binary format";
+ const char *human_readable = "show identify in readable format";
+ const char *namespace_id = "identifier of desired namespace";
+
+ enum nvme_print_flags flags;
+ struct nvme_cmd_set_independent_id_ns ns;
+ int err = -1, fd;
+
+ struct config {
+ __u32 namespace_id;
+ int raw_binary;
+ int human_readable;
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .namespace_id = 0,
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
+ OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable),
+ OPT_END()
+ };
+
+ err = fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ goto ret;
+
+ err = flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto close_fd;
+ if (cfg.raw_binary)
+ flags = BINARY;
+ if (cfg.human_readable)
+ flags |= VERBOSE;
+
+ if (!cfg.namespace_id) {
+ err = cfg.namespace_id = nvme_get_nsid(fd);
+ if (err < 0) {
+ perror("get-namespace-id");
+ goto close_fd;
+ }
+ }
+
+ err = nvme_cmd_set_independent_identify_ns(fd, cfg.namespace_id, &ns);
+ if (!err)
+ nvme_show_cmd_set_independent_id_ns(&ns, cfg.namespace_id, flags);
+ else if (err > 0)
+ nvme_show_status(err);
+ else
+ perror("I/O command set independent identify namespace");
+close_fd:
+ close(fd);
+ret:
+ return nvme_status_to_errno(err, false);
+}
+
static int id_ns_granularity(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Send an Identify Namespace Granularity List command to the "\
@@ -5244,7 +5640,7 @@ static int submit_io(int opcode, char *command, const char *desc,
gettimeofday(&start_time, NULL);
err = nvme_io(fd, opcode, cfg.namespace_id, cfg.start_block, cfg.block_count,
control, dsmgmt, cfg.ref_tag, cfg.app_tag, cfg.app_tag_mask,
- cfg.storage_tag, buffer, mbuffer);
+ cfg.storage_tag, buffer, buffer_size, mbuffer, mbuffer_size);
gettimeofday(&end_time, NULL);
if (cfg.latency)
printf(" latency: %s: %llu us\n",
@@ -5788,6 +6184,100 @@ static int rpmb_cmd(int argc, char **argv, struct command *cmd, struct plugin *p
return rpmb_cmd_option(argc, argv, cmd, plugin);
}
+static int lockdown_cmd(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "The Lockdown command is used to control the "\
+ "Command and Feature Lockdown capability which configures the "\
+ "prohibition or allowance of execution of the specified command "\
+ "or Set Features command targeting a specific Feature Identifier.";
+ const char *ofi_desc = "Opcode or Feature Identifier(OFI) "\
+ "specifies the command opcode or Set Features Feature Identifier "\
+ "identified by the Scope field.";
+ const char *ifc_desc = "[0-3] Interface (INF) field identifies the "\
+ "interfaces affected by this command.";
+ const char *prhbt_desc = "[0-1]Prohibit(PRHBT) bit specifies whether "\
+ "to prohibit or allow the command opcode or Set Features Feature "\
+ "Identifier specified by this command.";
+ const char *scp_desc = "[0-15]Scope(SCP) field specifies the contents "\
+ "of the Opcode or Feature Identifier field.";
+ const char *uuid_desc = "UUID Index - If this field is set to a non-zero "\
+ "value, then the value of this field is the index of a UUID in the UUID "\
+ "List that is used by the command.If this field is cleared to 0h,"\
+ "then no UUID index is specified";
+
+ int fd, err = -1;
+
+ struct config {
+ __u8 ofi;
+ __u8 ifc;
+ __u8 prhbt;
+ __u8 scp;
+ __u8 uuid;
+ };
+
+ struct config cfg = {
+ .ofi = 0,
+ .ifc = 0,
+ .prhbt = 0,
+ .scp = 0,
+ .uuid = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_BYTE("ofi", 'o', &cfg.ofi, ofi_desc),
+ OPT_BYTE("ifc", 'f', &cfg.ifc, ifc_desc),
+ OPT_BYTE("prhbt", 'p', &cfg.prhbt, prhbt_desc),
+ OPT_BYTE("scp", 's', &cfg.scp, scp_desc),
+ OPT_BYTE("uuid", 'U', &cfg.uuid, uuid_desc),
+ OPT_END()
+ };
+
+ err = fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ goto ret;
+
+ /* check for input arguement limit */
+ if (cfg.ifc > 3) {
+ fprintf(stderr, "invalid interface settings:%d\n", cfg.ifc);
+ errno = EINVAL;
+ err = -1;
+ goto close_fd;
+ }
+ if (cfg.prhbt > 1) {
+ fprintf(stderr, "invalid prohibit settings:%d\n", cfg.prhbt);
+ errno = EINVAL;
+ err = -1;
+ goto close_fd;
+ }
+ if (cfg.scp > 15) {
+ fprintf(stderr, "invalid scope settings:%d\n", cfg.scp);
+ errno = EINVAL;
+ err = -1;
+ goto close_fd;
+ }
+ if (cfg.uuid > 127) {
+ fprintf(stderr, "invalid UUID index settings:%d\n", cfg.uuid);
+ errno = EINVAL;
+ err = -1;
+ goto close_fd;
+ }
+
+ err = nvme_lockdown(fd, cfg.scp,cfg.prhbt,cfg.ifc,cfg.ofi,
+ cfg.uuid);
+
+ if (err < 0)
+ perror("lockdown");
+ else if (err > 0)
+ nvme_show_status(err);
+ else
+ printf("Lockdown Command is Successful\n");
+
+close_fd:
+ close(fd);
+ret:
+ return nvme_status_to_errno(err, false);
+}
+
static int passthru(int argc, char **argv, int ioctl_cmd, uint8_t cmd_type,
const char *desc, struct command *cmd)
{